wininet: Remove an unneeded NONAMELESSSTRUCT directive.
[wine.git] / dlls / wininet / urlcache.c
blob276132bcc3657bbf51aa7ae06d8770c553f16a84
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
27 #include "ws2tcpip.h"
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"
38 #include "wininet.h"
39 #include "winineti.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "shlwapi.h"
43 #include "shlobj.h"
44 #include "shellapi.h"
46 #include "internet.h"
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
53 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
54 static const char urlcache_ver[] = "0.2012001";
56 #ifndef CHAR_BIT
57 #define CHAR_BIT (8 * sizeof(CHAR))
58 #endif
60 #define ENTRY_START_OFFSET 0x4000
61 #define DIR_LENGTH 8
62 #define MAX_DIR_NO 0x20
63 #define BLOCKSIZE 128
64 #define HASHTABLE_SIZE 448
65 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
66 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
67 #define ALLOCATION_TABLE_OFFSET 0x250
68 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
69 #define MIN_BLOCK_NO 0x80
70 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
71 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
73 #define HASHTABLE_URL 0
74 #define HASHTABLE_DEL 1
75 #define HASHTABLE_LOCK 2
76 #define HASHTABLE_FREE 3
77 #define HASHTABLE_REDR 5
78 #define HASHTABLE_FLAG_BITS 6
80 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
81 #define INSTALLED_CACHE_ENTRY 0x10000000
82 #define GET_INSTALLED_ENTRY 0x200
83 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
85 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
87 #define FILETIME_SECOND 10000000
89 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
90 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
91 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
92 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
93 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
95 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
97 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
99 typedef struct
101 DWORD signature;
102 DWORD blocks_used; /* number of 128byte blocks used by this entry */
103 } entry_header;
105 typedef struct
107 entry_header header;
108 FILETIME modification_time;
109 FILETIME access_time;
110 WORD expire_date; /* expire date in dos format */
111 WORD expire_time; /* expire time in dos format */
112 DWORD unk1; /* usually zero */
113 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
114 DWORD unk2; /* usually zero */
115 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
116 DWORD unk3; /* usually 0x60 */
117 DWORD url_off; /* offset of start of url from start of entry */
118 BYTE cache_dir; /* index of cache directory this url is stored in */
119 BYTE unk4; /* usually zero */
120 WORD unk5; /* usually 0x1010 */
121 DWORD local_name_off; /* offset of start of local filename from start of entry */
122 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
123 DWORD header_info_off; /* offset of start of header info from start of entry */
124 DWORD header_info_size;
125 DWORD file_extension_off; /* offset of start of file extension from start of entry */
126 WORD sync_date; /* last sync date in dos format */
127 WORD sync_time; /* last sync time in dos format */
128 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
129 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
130 WORD write_date;
131 WORD write_time;
132 DWORD unk7; /* usually zero */
133 DWORD unk8; /* usually zero */
134 /* packing to dword align start of next field */
135 /* CHAR szSourceUrlName[]; (url) */
136 /* packing to dword align start of next field */
137 /* CHAR szLocalFileName[]; (local file name excluding path) */
138 /* packing to dword align start of next field */
139 /* CHAR szHeaderInfo[]; (header info) */
140 } entry_url;
142 struct hash_entry
144 DWORD key;
145 DWORD offset;
148 typedef struct
150 entry_header header;
151 DWORD next;
152 DWORD id;
153 struct hash_entry hash_table[HASHTABLE_SIZE];
154 } entry_hash_table;
156 typedef struct
158 char signature[28];
159 DWORD size;
160 DWORD hash_table_off;
161 DWORD capacity_in_blocks;
162 DWORD blocks_in_use;
163 DWORD unk1;
164 ULARGE_INTEGER cache_limit;
165 ULARGE_INTEGER cache_usage;
166 ULARGE_INTEGER exempt_usage;
167 DWORD dirs_no;
168 struct _directory_data
170 DWORD files_no;
171 char name[DIR_LENGTH];
172 } directory_data[MAX_DIR_NO];
173 DWORD options[0x21];
174 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
175 } urlcache_header;
177 typedef struct
179 HANDLE file;
180 CHAR url[1];
181 } stream_handle;
183 typedef struct
185 struct list entry; /* part of a list */
186 char *cache_prefix; /* string that has to be prefixed for this container to be used */
187 LPWSTR path; /* path to url container directory */
188 HANDLE mapping; /* handle of file mapping */
189 DWORD file_size; /* size of file when mapping was opened */
190 HANDLE mutex; /* handle of mutex */
191 DWORD default_entry_type;
192 } cache_container;
194 typedef struct
196 DWORD magic;
197 char *url_search_pattern;
198 DWORD container_idx;
199 DWORD hash_table_idx;
200 DWORD hash_entry_idx;
201 } find_handle;
203 /* List of all containers available */
204 static struct list UrlContainers = LIST_INIT(UrlContainers);
206 static inline char *heap_strdupWtoUTF8(LPCWSTR str)
208 char *ret = NULL;
210 if(str) {
211 DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
212 ret = heap_alloc(size);
213 if(ret)
214 WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
217 return ret;
220 /***********************************************************************
221 * urlcache_block_is_free (Internal)
223 * Is the specified block number free?
225 * RETURNS
226 * zero if free
227 * non-zero otherwise
230 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
232 BYTE mask = 1 << (block_number%CHAR_BIT);
233 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
236 /***********************************************************************
237 * urlcache_block_free (Internal)
239 * Marks the specified block as free
241 * CAUTION
242 * this function is not updating used blocks count
244 * RETURNS
245 * nothing
248 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
250 BYTE mask = ~(1 << (block_number%CHAR_BIT));
251 allocation_table[block_number/CHAR_BIT] &= mask;
254 /***********************************************************************
255 * urlcache_block_alloc (Internal)
257 * Marks the specified block as allocated
259 * CAUTION
260 * this function is not updating used blocks count
262 * RETURNS
263 * nothing
266 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
268 BYTE mask = 1 << (block_number%CHAR_BIT);
269 allocation_table[block_number/CHAR_BIT] |= mask;
272 /***********************************************************************
273 * urlcache_entry_alloc (Internal)
275 * Finds and allocates the first block of free space big enough and
276 * sets entry to point to it.
278 * RETURNS
279 * ERROR_SUCCESS when free memory block was found
280 * Any other Win32 error code if the entry could not be added
283 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
285 DWORD block, block_size;
287 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
289 block_size = 0;
290 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
291 && urlcache_block_is_free(header->allocation_table, block+block_size))
292 block_size++;
294 if(block_size == blocks_needed)
296 DWORD index;
298 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
300 for(index=0; index<blocks_needed; index++)
301 urlcache_block_alloc(header->allocation_table, block+index);
303 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
304 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
305 ((DWORD*)*entry)[index] = 0xdeadbeef;
306 (*entry)->blocks_used = blocks_needed;
308 header->blocks_in_use += blocks_needed;
309 return ERROR_SUCCESS;
313 return ERROR_HANDLE_DISK_FULL;
316 /***********************************************************************
317 * urlcache_entry_free (Internal)
319 * Deletes the specified entry and frees the space allocated to it
321 * RETURNS
322 * TRUE if it succeeded
323 * FALSE if it failed
326 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
328 DWORD start_block, block;
330 /* update allocation table */
331 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
332 for(block = start_block; block < start_block+entry->blocks_used; block++)
333 urlcache_block_free(header->allocation_table, block);
335 header->blocks_in_use -= entry->blocks_used;
336 return TRUE;
339 /***********************************************************************
340 * urlcache_create_hash_table (Internal)
342 * Creates a new hash table in free space and adds it to the chain of existing
343 * hash tables.
345 * RETURNS
346 * ERROR_SUCCESS if the hash table was created
347 * ERROR_DISK_FULL if the hash table could not be created
350 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
352 DWORD dwOffset, error;
353 int i;
355 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
356 return error;
358 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
360 if(hash_table_prev)
361 hash_table_prev->next = dwOffset;
362 else
363 header->hash_table_off = dwOffset;
365 (*hash_table)->header.signature = HASH_SIGNATURE;
366 (*hash_table)->next = 0;
367 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
368 for(i = 0; i < HASHTABLE_SIZE; i++) {
369 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
370 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
372 return ERROR_SUCCESS;
375 /***********************************************************************
376 * cache_container_create_object_name (Internal)
378 * Converts a path to a name suitable for use as a Win32 object name.
379 * Replaces '\\' characters in-place with the specified character
380 * (usually '_' or '!')
382 * RETURNS
383 * nothing
386 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
388 for (; *lpszPath; lpszPath++)
390 if (*lpszPath == '\\')
391 *lpszPath = replace;
395 /* Caller must hold container lock */
396 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
398 static const WCHAR mapping_name_format[]
399 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
400 WCHAR mapping_name[MAX_PATH];
401 HANDLE mapping;
403 wsprintfW(mapping_name, mapping_name_format, path, size);
404 cache_container_create_object_name(mapping_name, '_');
406 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
407 if(mapping) {
408 if(validate) *validate = FALSE;
409 return mapping;
412 if(validate) *validate = TRUE;
413 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
416 /* Caller must hold container lock */
417 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
419 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
420 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
421 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
422 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
423 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
424 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
426 DWORD file_size = FILE_SIZE(blocks_no);
427 WCHAR dir_path[MAX_PATH], *dir_name;
428 entry_hash_table *hashtable_entry;
429 urlcache_header *header;
430 HANDLE mapping;
431 FILETIME ft;
432 HKEY key;
433 int i, j;
435 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
436 return GetLastError();
438 if(!SetEndOfFile(file))
439 return GetLastError();
441 mapping = cache_container_map_index(file, container->path, file_size, NULL);
442 if(!mapping)
443 return GetLastError();
445 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
446 if(!header) {
447 CloseHandle(mapping);
448 return GetLastError();
451 if(blocks_no != MIN_BLOCK_NO) {
452 if(file_size > header->size)
453 memset((char*)header+header->size, 0, file_size-header->size);
454 header->size = file_size;
455 header->capacity_in_blocks = blocks_no;
457 UnmapViewOfFile(header);
458 CloseHandle(container->mapping);
459 container->mapping = mapping;
460 container->file_size = file_size;
461 return ERROR_SUCCESS;
464 memset(header, 0, file_size);
465 /* First set some constants and defaults in the header */
466 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
467 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
468 header->size = file_size;
469 header->capacity_in_blocks = blocks_no;
470 /* 127MB - taken from default for Windows 2000 */
471 header->cache_limit.QuadPart = 0x07ff5400;
472 /* Copied from a Windows 2000 cache index */
473 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
475 /* If the registry has a cache size set, use the registry value */
476 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
477 DWORD dw, len = sizeof(dw), keytype;
479 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
480 keytype == REG_DWORD)
481 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
482 RegCloseKey(key);
485 urlcache_create_hash_table(header, NULL, &hashtable_entry);
487 /* Last step - create the directories */
488 strcpyW(dir_path, container->path);
489 dir_name = dir_path + strlenW(dir_path);
490 dir_name[8] = 0;
492 GetSystemTimeAsFileTime(&ft);
494 for(i=0; i<header->dirs_no; ++i) {
495 header->directory_data[i].files_no = 0;
496 for(j=0;; ++j) {
497 ULONGLONG n = ft.dwHighDateTime;
498 int k;
500 /* Generate a file name to attempt to create.
501 * This algorithm will create what will appear
502 * to be random and unrelated directory names
503 * of up to 9 characters in length.
505 n <<= 32;
506 n += ft.dwLowDateTime;
507 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
509 for(k = 0; k < 8; ++k) {
510 int r = (n % 36);
512 /* Dividing by a prime greater than 36 helps
513 * with the appearance of randomness
515 n /= 37;
517 if(r < 10)
518 dir_name[k] = '0' + r;
519 else
520 dir_name[k] = 'A' + (r - 10);
523 if(CreateDirectoryW(dir_path, 0)) {
524 /* The following is OK because we generated an
525 * 8 character directory name made from characters
526 * [A-Z0-9], which are equivalent for all code
527 * pages and for UTF-16
529 for (k = 0; k < 8; ++k)
530 header->directory_data[i].name[k] = dir_name[k];
531 break;
532 }else if(j >= 255) {
533 /* Give up. The most likely cause of this
534 * is a full disk, but whatever the cause
535 * is, it should be more than apparent that
536 * we won't succeed.
538 UnmapViewOfFile(header);
539 CloseHandle(mapping);
540 return GetLastError();
545 UnmapViewOfFile(header);
546 CloseHandle(container->mapping);
547 container->mapping = mapping;
548 container->file_size = file_size;
549 return ERROR_SUCCESS;
552 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
554 DWORD allocation_size, count_bits, i;
556 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
557 return FALSE;
559 if(file_size != header->size)
560 return FALSE;
562 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
563 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
564 return FALSE;
566 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
567 return FALSE;
569 allocation_size = 0;
570 for(i=0; i<header->capacity_in_blocks/8; i++) {
571 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
572 if(count_bits & 1)
573 allocation_size++;
576 if(allocation_size != header->blocks_in_use)
577 return FALSE;
579 for(; i<ALLOCATION_TABLE_SIZE; i++) {
580 if(header->allocation_table[i])
581 return FALSE;
584 return TRUE;
587 /***********************************************************************
588 * cache_container_open_index (Internal)
590 * Opens the index file and saves mapping handle
592 * RETURNS
593 * ERROR_SUCCESS if succeeded
594 * Any other Win32 error code if failed
597 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
599 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
601 HANDLE file;
602 WCHAR index_path[MAX_PATH];
603 DWORD file_size;
604 BOOL validate;
606 WaitForSingleObject(container->mutex, INFINITE);
608 if(container->mapping) {
609 ReleaseMutex(container->mutex);
610 return ERROR_SUCCESS;
613 strcpyW(index_path, container->path);
614 strcatW(index_path, index_dat);
616 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
617 if(file == INVALID_HANDLE_VALUE) {
618 /* Maybe the directory wasn't there? Try to create it */
619 if(CreateDirectoryW(container->path, 0))
620 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
622 if(file == INVALID_HANDLE_VALUE) {
623 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
624 ReleaseMutex(container->mutex);
625 return GetLastError();
628 file_size = GetFileSize(file, NULL);
629 if(file_size == INVALID_FILE_SIZE) {
630 CloseHandle(file);
631 ReleaseMutex(container->mutex);
632 return GetLastError();
635 if(blocks_no < MIN_BLOCK_NO)
636 blocks_no = MIN_BLOCK_NO;
637 else if(blocks_no > MAX_BLOCK_NO)
638 blocks_no = MAX_BLOCK_NO;
640 if(file_size < FILE_SIZE(blocks_no)) {
641 DWORD ret = cache_container_set_size(container, file, blocks_no);
642 CloseHandle(file);
643 ReleaseMutex(container->mutex);
644 return ret;
647 container->file_size = file_size;
648 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
649 CloseHandle(file);
650 if(container->mapping && validate) {
651 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
653 if(header && !cache_container_is_valid(header, file_size)) {
654 WARN("detected old or broken index.dat file\n");
655 UnmapViewOfFile(header);
656 FreeUrlCacheSpaceW(container->path, 100, 0);
657 }else if(header) {
658 UnmapViewOfFile(header);
659 }else {
660 CloseHandle(container->mapping);
661 container->mapping = NULL;
665 if(!container->mapping)
667 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
668 ReleaseMutex(container->mutex);
669 return GetLastError();
672 ReleaseMutex(container->mutex);
673 return ERROR_SUCCESS;
676 /***********************************************************************
677 * cache_container_close_index (Internal)
679 * Closes the index
681 * RETURNS
682 * nothing
685 static void cache_container_close_index(cache_container *pContainer)
687 CloseHandle(pContainer->mapping);
688 pContainer->mapping = NULL;
691 static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
692 DWORD default_entry_type, LPWSTR mutex_name)
694 cache_container *pContainer = heap_alloc(sizeof(cache_container));
695 int cache_prefix_len = strlen(cache_prefix);
697 if (!pContainer)
699 return FALSE;
702 pContainer->mapping = NULL;
703 pContainer->file_size = 0;
704 pContainer->default_entry_type = default_entry_type;
706 pContainer->path = heap_strdupW(path);
707 if (!pContainer->path)
709 heap_free(pContainer);
710 return FALSE;
713 pContainer->cache_prefix = heap_alloc(cache_prefix_len+1);
714 if (!pContainer->cache_prefix)
716 heap_free(pContainer->path);
717 heap_free(pContainer);
718 return FALSE;
721 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
723 CharLowerW(mutex_name);
724 cache_container_create_object_name(mutex_name, '!');
726 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
728 ERR("couldn't create mutex (error is %d)\n", GetLastError());
729 heap_free(pContainer->path);
730 heap_free(pContainer);
731 return FALSE;
734 list_add_head(&UrlContainers, &pContainer->entry);
736 return TRUE;
739 static void cache_container_delete_container(cache_container *pContainer)
741 list_remove(&pContainer->entry);
743 cache_container_close_index(pContainer);
744 CloseHandle(pContainer->mutex);
745 heap_free(pContainer->path);
746 heap_free(pContainer->cache_prefix);
747 heap_free(pContainer);
750 static void cache_containers_init(void)
752 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
753 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
754 static const WCHAR CookieSuffix[] = {0};
755 static const struct
757 int nFolder; /* CSIDL_* constant */
758 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
759 const char *cache_prefix; /* prefix used to reference the container */
760 DWORD default_entry_type;
761 } DefaultContainerData[] =
763 { CSIDL_INTERNET_CACHE, UrlSuffix, "", NORMAL_CACHE_ENTRY },
764 { CSIDL_HISTORY, HistorySuffix, "Visited:", URLHISTORY_CACHE_ENTRY },
765 { CSIDL_COOKIES, CookieSuffix, "Cookie:", COOKIE_CACHE_ENTRY },
767 DWORD i;
769 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
771 WCHAR wszCachePath[MAX_PATH];
772 WCHAR wszMutexName[MAX_PATH];
773 int path_len, suffix_len;
774 BOOL def_char;
776 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
778 ERR("Couldn't get path for default container %u\n", i);
779 continue;
781 path_len = strlenW(wszCachePath);
782 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
784 if (path_len + suffix_len + 2 > MAX_PATH)
786 ERR("Path too long\n");
787 continue;
790 wszCachePath[path_len] = '\\';
791 wszCachePath[path_len+1] = 0;
793 strcpyW(wszMutexName, wszCachePath);
795 if (suffix_len)
797 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
798 wszCachePath[path_len + suffix_len + 1] = '\\';
799 wszCachePath[path_len + suffix_len + 2] = '\0';
802 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
803 NULL, 0, NULL, &def_char) || def_char)
805 WCHAR tmp[MAX_PATH];
807 /* cannot convert path to ANSI code page */
808 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
809 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
810 NULL, 0, NULL, &def_char) || def_char)
811 ERR("Can't create container path accessible by ANSI functions\n");
812 else
813 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
816 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
817 DefaultContainerData[i].default_entry_type, wszMutexName);
821 static void cache_containers_free(void)
823 while(!list_empty(&UrlContainers))
824 cache_container_delete_container(
825 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
829 static DWORD cache_containers_find(const char *url, cache_container **ret)
831 cache_container *container;
833 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
835 if(!url)
836 return ERROR_INVALID_PARAMETER;
838 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
840 int prefix_len = strlen(container->cache_prefix);
842 if(!strncmp(container->cache_prefix, url, prefix_len)) {
843 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
844 *ret = container;
845 return ERROR_SUCCESS;
849 ERR("no container found\n");
850 return ERROR_FILE_NOT_FOUND;
853 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
855 DWORD i = 0;
856 cache_container *container;
858 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
860 /* non-NULL search pattern only returns one container ever */
861 if (search_pattern && index > 0)
862 return FALSE;
864 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
866 if (search_pattern)
868 if (!strcmp(container->cache_prefix, search_pattern))
870 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
871 *ret = container;
872 return TRUE;
875 else
877 if (i == index)
879 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
880 *ret = container;
881 return TRUE;
884 i++;
886 return FALSE;
889 /***********************************************************************
890 * cache_container_lock_index (Internal)
892 * Locks the index for system-wide exclusive access.
894 * RETURNS
895 * Cache file header if successful
896 * NULL if failed and calls SetLastError.
898 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
900 BYTE index;
901 LPVOID pIndexData;
902 urlcache_header* pHeader;
903 DWORD error;
905 /* acquire mutex */
906 WaitForSingleObject(pContainer->mutex, INFINITE);
908 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
910 if (!pIndexData)
912 ReleaseMutex(pContainer->mutex);
913 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
914 return NULL;
916 pHeader = (urlcache_header*)pIndexData;
918 /* file has grown - we need to remap to prevent us getting
919 * access violations when we try and access beyond the end
920 * of the memory mapped file */
921 if (pHeader->size != pContainer->file_size)
923 UnmapViewOfFile( pHeader );
924 cache_container_close_index(pContainer);
925 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
926 if (error != ERROR_SUCCESS)
928 ReleaseMutex(pContainer->mutex);
929 SetLastError(error);
930 return NULL;
932 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
934 if (!pIndexData)
936 ReleaseMutex(pContainer->mutex);
937 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
938 return NULL;
940 pHeader = (urlcache_header*)pIndexData;
943 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
945 for (index = 0; index < pHeader->dirs_no; index++)
947 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
950 return pHeader;
953 /***********************************************************************
954 * cache_container_unlock_index (Internal)
957 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
959 /* release mutex */
960 ReleaseMutex(pContainer->mutex);
961 return UnmapViewOfFile(pHeader);
964 /***********************************************************************
965 * urlcache_create_file_pathW (Internal)
967 * Copies the full path to the specified buffer given the local file
968 * name and the index of the directory it is in. Always sets value in
969 * lpBufferSize to the required buffer size (in bytes).
971 * RETURNS
972 * TRUE if the buffer was big enough
973 * FALSE if the buffer was too small
976 static BOOL urlcache_create_file_pathW(
977 const cache_container *pContainer,
978 const urlcache_header *pHeader,
979 LPCSTR szLocalFileName,
980 BYTE Directory,
981 LPWSTR wszPath,
982 LPLONG lpBufferSize)
984 LONG nRequired;
985 int path_len = strlenW(pContainer->path);
986 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
987 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
989 *lpBufferSize = 0;
990 return FALSE;
993 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
994 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
995 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
996 if (nRequired <= *lpBufferSize)
998 int dir_len;
1000 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1001 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1003 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1004 wszPath[dir_len + path_len] = '\\';
1005 dir_len++;
1007 else
1009 dir_len = 0;
1011 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
1012 *lpBufferSize = nRequired;
1013 return TRUE;
1015 *lpBufferSize = nRequired;
1016 return FALSE;
1019 /***********************************************************************
1020 * urlcache_create_file_pathA (Internal)
1022 * Copies the full path to the specified buffer given the local file
1023 * name and the index of the directory it is in. Always sets value in
1024 * lpBufferSize to the required buffer size.
1026 * RETURNS
1027 * TRUE if the buffer was big enough
1028 * FALSE if the buffer was too small
1031 static BOOL urlcache_create_file_pathA(
1032 const cache_container *pContainer,
1033 const urlcache_header *pHeader,
1034 LPCSTR szLocalFileName,
1035 BYTE Directory,
1036 LPSTR szPath,
1037 LPLONG lpBufferSize)
1039 LONG nRequired;
1040 int path_len, file_name_len, dir_len;
1042 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1044 *lpBufferSize = 0;
1045 return FALSE;
1048 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1049 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1050 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1051 dir_len = DIR_LENGTH+1;
1052 else
1053 dir_len = 0;
1055 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1056 if (nRequired <= *lpBufferSize)
1058 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1059 if(dir_len) {
1060 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1061 szPath[path_len + dir_len-1] = '\\';
1063 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1064 *lpBufferSize = nRequired;
1065 return TRUE;
1067 *lpBufferSize = nRequired;
1068 return FALSE;
1071 /* Just like FileTimeToDosDateTime, except that it also maps the special
1072 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1074 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1075 WORD *fattime)
1077 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1078 *fatdate = *fattime = 0;
1079 else
1080 FileTimeToDosDateTime(ft, fatdate, fattime);
1083 /***********************************************************************
1084 * urlcache_delete_file (Internal)
1086 static DWORD urlcache_delete_file(const cache_container *container,
1087 urlcache_header *header, entry_url *url_entry)
1089 WIN32_FILE_ATTRIBUTE_DATA attr;
1090 WCHAR path[MAX_PATH];
1091 LONG path_size = sizeof(path);
1092 DWORD err;
1093 WORD date, time;
1095 if(!url_entry->local_name_off)
1096 goto succ;
1098 if(!urlcache_create_file_pathW(container, header,
1099 (LPCSTR)url_entry+url_entry->local_name_off,
1100 url_entry->cache_dir, path, &path_size))
1101 goto succ;
1103 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1104 goto succ;
1105 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1106 if(date != url_entry->write_date || time != url_entry->write_time)
1107 goto succ;
1109 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1110 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1111 return err;
1113 succ:
1114 if (url_entry->cache_dir < header->dirs_no)
1116 if (header->directory_data[url_entry->cache_dir].files_no)
1117 header->directory_data[url_entry->cache_dir].files_no--;
1119 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1121 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1122 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1123 else
1124 header->exempt_usage.QuadPart = 0;
1126 else
1128 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1129 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1130 else
1131 header->cache_usage.QuadPart = 0;
1134 return ERROR_SUCCESS;
1137 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1139 DWORD *leak_off;
1140 BOOL freed = FALSE;
1142 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1143 while(*leak_off) {
1144 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1146 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1147 *leak_off = url_entry->exempt_delta;
1148 urlcache_entry_free(header, &url_entry->header);
1149 freed = TRUE;
1150 }else {
1151 leak_off = &url_entry->exempt_delta;
1155 return freed;
1158 /***********************************************************************
1159 * cache_container_clean_index (Internal)
1161 * This function is meant to make place in index file by removing leaked
1162 * files entries and resizing the file.
1164 * CAUTION: file view may get mapped to new memory
1166 * RETURNS
1167 * ERROR_SUCCESS when new memory is available
1168 * error code otherwise
1170 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1172 urlcache_header *header = *file_view;
1173 DWORD ret;
1175 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1177 if(urlcache_clean_leaked_entries(container, header))
1178 return ERROR_SUCCESS;
1180 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1181 WARN("index file has maximal size\n");
1182 return ERROR_NOT_ENOUGH_MEMORY;
1185 cache_container_close_index(container);
1186 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1187 if(ret != ERROR_SUCCESS)
1188 return ret;
1189 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1190 if(!header)
1191 return GetLastError();
1193 UnmapViewOfFile(*file_view);
1194 *file_view = header;
1195 return ERROR_SUCCESS;
1198 /* Just like DosDateTimeToFileTime, except that it also maps the special
1199 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1201 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1202 FILETIME *ft)
1204 if (!fatdate && !fattime)
1205 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1206 else
1207 DosDateTimeToFileTime(fatdate, fattime, ft);
1210 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1212 URL_COMPONENTSA uc;
1213 DWORD len, part_len;
1214 WCHAR *host_name;
1216 memset(&uc, 0, sizeof(uc));
1217 uc.dwStructSize = sizeof(uc);
1218 uc.dwHostNameLength = 1;
1219 if(!InternetCrackUrlA(url, 0, 0, &uc))
1220 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1222 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1223 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1225 if(!decoded_url)
1226 decoded_len = 0;
1228 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1229 if(!len)
1230 return 0;
1231 if(decoded_url)
1232 decoded_len -= len;
1234 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1235 if(!host_name)
1236 return 0;
1237 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1238 host_name, uc.dwHostNameLength)) {
1239 heap_free(host_name);
1240 return 0;
1242 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1243 decoded_url ? decoded_url+len : NULL, decoded_len);
1244 heap_free(host_name);
1245 if(!part_len) {
1246 SetLastError(ERROR_INTERNET_INVALID_URL);
1247 return 0;
1249 len += part_len;
1250 if(decoded_url)
1251 decoded_len -= part_len;
1253 part_len = MultiByteToWideChar(CP_UTF8, 0,
1254 uc.lpszHostName+uc.dwHostNameLength,
1255 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1256 if(!part_len)
1257 return 0;
1258 len += part_len;
1260 return len;
1263 /***********************************************************************
1264 * urlcache_copy_entry (Internal)
1266 * Copies an entry from the cache index file to the Win32 structure
1268 * RETURNS
1269 * ERROR_SUCCESS if the buffer was big enough
1270 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1273 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1274 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1276 int url_len;
1277 DWORD size = sizeof(*entry_info);
1279 if(*info_size >= size) {
1280 entry_info->lpHeaderInfo = NULL;
1281 entry_info->lpszFileExtension = NULL;
1282 entry_info->lpszLocalFileName = NULL;
1283 entry_info->lpszSourceUrlName = NULL;
1284 entry_info->CacheEntryType = url_entry->cache_entry_type;
1285 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1286 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1287 entry_info->dwHitRate = url_entry->hit_rate;
1288 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1289 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1290 entry_info->dwStructSize = sizeof(*entry_info);
1291 entry_info->dwUseCount = url_entry->use_count;
1292 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1293 entry_info->LastAccessTime = url_entry->access_time;
1294 entry_info->LastModifiedTime = url_entry->modification_time;
1295 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1298 if(unicode)
1299 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1300 else
1301 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1302 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1304 if(*info_size >= size) {
1305 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1307 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1308 if(unicode)
1309 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1310 else
1311 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1314 if(size%4 && size<*info_size)
1315 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1316 size = DWORD_ALIGN(size);
1318 if(url_entry->local_name_off) {
1319 LONG file_name_size;
1320 LPSTR file_name;
1321 file_name = (LPSTR)entry_info+size;
1322 file_name_size = *info_size-size;
1323 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)) ||
1324 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off, url_entry->cache_dir, file_name, &file_name_size))) {
1325 entry_info->lpszLocalFileName = file_name;
1327 size += file_name_size;
1329 if(size%4 && size<*info_size)
1330 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1331 size = DWORD_ALIGN(size);
1334 if(url_entry->header_info_off) {
1335 DWORD header_len;
1337 if(unicode)
1338 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1339 url_entry->header_info_size, NULL, 0);
1340 else
1341 header_len = url_entry->header_info_size;
1342 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1344 if(*info_size >= size) {
1345 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1346 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1347 if(unicode)
1348 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1349 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1350 else
1351 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1353 if(size%4 && size<*info_size)
1354 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1355 size = DWORD_ALIGN(size);
1358 if(url_entry->file_extension_off) {
1359 int ext_len;
1361 if(unicode)
1362 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1363 else
1364 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1365 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1367 if(*info_size >= size) {
1368 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1369 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1370 if(unicode)
1371 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1372 else
1373 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1376 if(size%4 && size<*info_size)
1377 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1378 size = DWORD_ALIGN(size);
1381 if(size > *info_size) {
1382 *info_size = size;
1383 return ERROR_INSUFFICIENT_BUFFER;
1385 *info_size = size;
1386 return ERROR_SUCCESS;
1389 /***********************************************************************
1390 * urlcache_set_entry_info (Internal)
1392 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1393 * according to the flags set by field_control.
1395 * RETURNS
1396 * ERROR_SUCCESS if the buffer was big enough
1397 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1400 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1402 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1403 url_entry->access_time = entry_info->LastAccessTime;
1404 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1405 url_entry->cache_entry_type = entry_info->CacheEntryType;
1406 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1407 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1408 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1409 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1410 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1411 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1412 if (field_control & CACHE_ENTRY_HITRATE_FC)
1413 url_entry->hit_rate = entry_info->dwHitRate;
1414 if (field_control & CACHE_ENTRY_MODTIME_FC)
1415 url_entry->modification_time = entry_info->LastModifiedTime;
1416 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1417 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1419 return ERROR_SUCCESS;
1422 /***********************************************************************
1423 * urlcache_hash_key (Internal)
1425 * Returns the hash key for a given string
1427 * RETURNS
1428 * hash key for the string
1431 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1433 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1434 * but the algorithm and result are not the same!
1436 static const unsigned char lookupTable[256] =
1438 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1439 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1440 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1441 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1442 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1443 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1444 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1445 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1446 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1447 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1448 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1449 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1450 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1451 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1452 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1453 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1454 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1455 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1456 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1457 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1458 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1459 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1460 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1461 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1462 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1463 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1464 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1465 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1466 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1467 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1468 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1469 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1471 BYTE key[4];
1472 DWORD i;
1474 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1475 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1477 for (lpszKey++; *lpszKey; lpszKey++)
1479 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1480 key[i] = lookupTable[*lpszKey ^ key[i]];
1483 return *(DWORD *)key;
1486 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1488 if(!dwOffset)
1489 return NULL;
1490 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1493 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1495 /* structure of hash table:
1496 * 448 entries divided into 64 blocks
1497 * each block therefore contains a chain of 7 key/offset pairs
1498 * how position in table is calculated:
1499 * 1. the url is hashed in helper function
1500 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1501 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1503 * note:
1504 * there can be multiple hash tables in the file and the offset to
1505 * the next one is stored in the header of the hash table
1507 DWORD key = urlcache_hash_key(lpszUrl);
1508 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1509 entry_hash_table* pHashEntry;
1510 DWORD id = 0;
1512 key >>= HASHTABLE_FLAG_BITS;
1514 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1515 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1517 int i;
1518 if (pHashEntry->id != id++)
1520 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1521 continue;
1523 /* make sure that it is in fact a hash entry */
1524 if (pHashEntry->header.signature != HASH_SIGNATURE)
1526 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1527 continue;
1530 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1532 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1533 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1535 /* FIXME: we should make sure that this is the right element
1536 * before returning and claiming that it is. We can do this
1537 * by doing a simple compare between the URL we were given
1538 * and the URL stored in the entry. However, this assumes
1539 * we know the format of all the entries stored in the
1540 * hash table */
1541 *ppHashEntry = pHashElement;
1542 return TRUE;
1546 return FALSE;
1549 /***********************************************************************
1550 * urlcache_hash_entry_set_flags (Internal)
1552 * Sets special bits in hash key
1554 * RETURNS
1555 * nothing
1558 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1560 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1563 /***********************************************************************
1564 * urlcache_hash_entry_delete (Internal)
1566 * Searches all the hash tables in the index for the given URL and
1567 * then if found deletes the entry.
1569 * RETURNS
1570 * TRUE if the entry was found
1571 * FALSE if the entry could not be found
1574 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1576 pHashEntry->key = HASHTABLE_DEL;
1577 return TRUE;
1580 /***********************************************************************
1581 * urlcache_hash_entry_create (Internal)
1583 * Searches all the hash tables for a free slot based on the offset
1584 * generated from the hash key. If a free slot is found, the offset and
1585 * key are entered into the hash table.
1587 * RETURNS
1588 * ERROR_SUCCESS if the entry was added
1589 * Any other Win32 error code if the entry could not be added
1592 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1594 /* see urlcache_find_hash_entry for structure of hash tables */
1596 DWORD key = urlcache_hash_key(lpszUrl);
1597 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1598 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1599 DWORD id = 0;
1600 DWORD error;
1602 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1604 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1605 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1607 int i;
1608 pHashPrev = pHashEntry;
1610 if (pHashEntry->id != id++)
1612 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1613 break;
1615 /* make sure that it is in fact a hash entry */
1616 if (pHashEntry->header.signature != HASH_SIGNATURE)
1618 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1619 break;
1622 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1624 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1625 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1627 pHashElement->key = key;
1628 pHashElement->offset = dwOffsetEntry;
1629 return ERROR_SUCCESS;
1633 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1634 if (error != ERROR_SUCCESS)
1635 return error;
1637 pHashEntry->hash_table[offset].key = key;
1638 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1639 return ERROR_SUCCESS;
1642 /***********************************************************************
1643 * urlcache_enum_hash_tables (Internal)
1645 * Enumerates the hash tables in a container.
1647 * RETURNS
1648 * TRUE if an entry was found
1649 * FALSE if there are no more tables to enumerate.
1652 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1654 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1655 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1657 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1658 if ((*ppHashEntry)->id != *id)
1659 continue;
1660 /* make sure that it is in fact a hash entry */
1661 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1663 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1664 (*id)++;
1665 continue;
1668 TRACE("hash table number %d found\n", *id);
1669 return TRUE;
1671 return FALSE;
1674 /***********************************************************************
1675 * urlcache_enum_hash_table_entries (Internal)
1677 * Enumerates entries in a hash table and returns the next non-free entry.
1679 * RETURNS
1680 * TRUE if an entry was found
1681 * FALSE if the hash table is empty or there are no more entries to
1682 * enumerate.
1685 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1686 DWORD * index, const struct hash_entry **ppHashEntry)
1688 for (; *index < HASHTABLE_SIZE ; (*index)++)
1690 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1691 continue;
1693 *ppHashEntry = &pHashEntry->hash_table[*index];
1694 TRACE("entry found %d\n", *index);
1695 return TRUE;
1697 TRACE("no more entries (%d)\n", *index);
1698 return FALSE;
1701 /***********************************************************************
1702 * cache_container_delete_dir (Internal)
1704 * Erase a directory containing an URL cache.
1706 * RETURNS
1707 * TRUE success, FALSE failure/aborted.
1710 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1712 DWORD path_len;
1713 WCHAR path[MAX_PATH + 1];
1714 SHFILEOPSTRUCTW shfos;
1715 int ret;
1717 path_len = strlenW(lpszPath);
1718 if (path_len >= MAX_PATH)
1719 return FALSE;
1720 strcpyW(path, lpszPath);
1721 path[path_len + 1] = 0; /* double-NUL-terminate path */
1723 shfos.hwnd = NULL;
1724 shfos.wFunc = FO_DELETE;
1725 shfos.pFrom = path;
1726 shfos.pTo = NULL;
1727 shfos.fFlags = FOF_NOCONFIRMATION;
1728 shfos.fAnyOperationsAborted = FALSE;
1729 ret = SHFileOperationW(&shfos);
1730 if (ret)
1731 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1732 return !(ret || shfos.fAnyOperationsAborted);
1735 /***********************************************************************
1736 * urlcache_hash_entry_is_locked (Internal)
1738 * Checks if entry is locked. Unlocks it if possible.
1740 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1742 FILETIME cur_time;
1743 ULARGE_INTEGER acc_time, time;
1745 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1746 return FALSE;
1748 GetSystemTimeAsFileTime(&cur_time);
1749 time.u.LowPart = cur_time.dwLowDateTime;
1750 time.u.HighPart = cur_time.dwHighDateTime;
1752 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1753 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1755 time.QuadPart -= acc_time.QuadPart;
1757 /* check if entry was locked for at least a day */
1758 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1759 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1760 url_entry->use_count = 0;
1761 return FALSE;
1764 return TRUE;
1767 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1768 DWORD *size, DWORD flags, BOOL unicode)
1770 urlcache_header *header;
1771 struct hash_entry *hash_entry;
1772 const entry_url *url_entry;
1773 cache_container *container;
1774 DWORD error;
1776 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1778 if(flags & ~GET_INSTALLED_ENTRY)
1779 FIXME("ignoring unsupported flags: %x\n", flags);
1781 error = cache_containers_find(url, &container);
1782 if(error != ERROR_SUCCESS) {
1783 SetLastError(error);
1784 return FALSE;
1787 error = cache_container_open_index(container, MIN_BLOCK_NO);
1788 if(error != ERROR_SUCCESS) {
1789 SetLastError(error);
1790 return FALSE;
1793 if(!(header = cache_container_lock_index(container)))
1794 return FALSE;
1796 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1797 cache_container_unlock_index(container, header);
1798 WARN("entry %s not found!\n", debugstr_a(url));
1799 SetLastError(ERROR_FILE_NOT_FOUND);
1800 return FALSE;
1803 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1804 if(url_entry->header.signature != URL_SIGNATURE) {
1805 cache_container_unlock_index(container, header);
1806 FIXME("Trying to retrieve entry of unknown format %s\n",
1807 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1808 SetLastError(ERROR_FILE_NOT_FOUND);
1809 return FALSE;
1812 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1813 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1814 url_entry->header_info_off, url_entry->header_info_size));
1816 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1817 cache_container_unlock_index(container, header);
1818 SetLastError(ERROR_FILE_NOT_FOUND);
1819 return FALSE;
1822 if(size) {
1823 if(!entry_info)
1824 *size = 0;
1826 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1827 if(error != ERROR_SUCCESS) {
1828 cache_container_unlock_index(container, header);
1829 SetLastError(error);
1830 return FALSE;
1832 if(url_entry->local_name_off)
1833 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1836 cache_container_unlock_index(container, header);
1837 return TRUE;
1840 /***********************************************************************
1841 * GetUrlCacheEntryInfoExA (WININET.@)
1844 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1845 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1846 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1847 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1849 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1850 ERR("Reserved value was not 0\n");
1851 SetLastError(ERROR_INVALID_PARAMETER);
1852 return FALSE;
1855 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1856 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1859 /***********************************************************************
1860 * GetUrlCacheEntryInfoA (WININET.@)
1863 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1864 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1865 LPDWORD lpdwCacheEntryInfoBufferSize)
1867 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1868 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1871 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1873 URL_COMPONENTSW uc;
1874 DWORD len, part_len;
1875 WCHAR *punycode;
1877 TRACE("%s\n", debugstr_w(url));
1879 memset(&uc, 0, sizeof(uc));
1880 uc.dwStructSize = sizeof(uc);
1881 uc.dwHostNameLength = 1;
1882 if(!InternetCrackUrlW(url, 0, 0, &uc))
1883 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1885 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1886 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1888 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1889 encoded_url, encoded_len, NULL, NULL);
1890 if(!len)
1891 return 0;
1892 if(encoded_url)
1893 encoded_len -= len;
1895 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1896 if(!part_len) {
1897 SetLastError(ERROR_INTERNET_INVALID_URL);
1898 return 0;
1901 punycode = heap_alloc(part_len*sizeof(WCHAR));
1902 if(!punycode)
1903 return 0;
1905 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1906 if(!part_len) {
1907 heap_free(punycode);
1908 return 0;
1911 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1912 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1913 heap_free(punycode);
1914 if(!part_len)
1915 return 0;
1916 if(encoded_url)
1917 encoded_len -= part_len;
1918 len += part_len;
1920 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1921 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1922 if(!part_len)
1923 return 0;
1924 len += part_len;
1926 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1927 return len;
1930 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1932 DWORD encoded_len;
1933 char *ret;
1935 encoded_len = urlcache_encode_url(url, NULL, 0);
1936 if(!encoded_len)
1937 return FALSE;
1939 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1940 if(!ret)
1941 return FALSE;
1943 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1944 if(!encoded_len) {
1945 heap_free(ret);
1946 return FALSE;
1949 *encoded_url = ret;
1950 return TRUE;
1953 /***********************************************************************
1954 * GetUrlCacheEntryInfoExW (WININET.@)
1957 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1958 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1959 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1960 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1962 char *url;
1963 BOOL ret;
1965 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1966 ERR("Reserved value was not 0\n");
1967 SetLastError(ERROR_INVALID_PARAMETER);
1968 return FALSE;
1971 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1972 dwFlags &= ~GET_INSTALLED_ENTRY;
1974 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1975 return FALSE;
1977 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1978 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1979 heap_free(url);
1980 return ret;
1983 /***********************************************************************
1984 * GetUrlCacheEntryInfoW (WININET.@)
1987 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1988 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1989 LPDWORD lpdwCacheEntryInfoBufferSize)
1991 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1992 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1995 /***********************************************************************
1996 * SetUrlCacheEntryInfoA (WININET.@)
1998 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1999 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2000 DWORD dwFieldControl)
2002 urlcache_header *pHeader;
2003 struct hash_entry *pHashEntry;
2004 entry_header *pEntry;
2005 cache_container *pContainer;
2006 DWORD error;
2008 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2010 error = cache_containers_find(lpszUrlName, &pContainer);
2011 if (error != ERROR_SUCCESS)
2013 SetLastError(error);
2014 return FALSE;
2017 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2018 if (error != ERROR_SUCCESS)
2020 SetLastError(error);
2021 return FALSE;
2024 if (!(pHeader = cache_container_lock_index(pContainer)))
2025 return FALSE;
2027 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2029 cache_container_unlock_index(pContainer, pHeader);
2030 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2031 SetLastError(ERROR_FILE_NOT_FOUND);
2032 return FALSE;
2035 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2036 if (pEntry->signature != URL_SIGNATURE)
2038 cache_container_unlock_index(pContainer, pHeader);
2039 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2040 SetLastError(ERROR_FILE_NOT_FOUND);
2041 return FALSE;
2044 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2046 cache_container_unlock_index(pContainer, pHeader);
2048 return TRUE;
2051 /***********************************************************************
2052 * SetUrlCacheEntryInfoW (WININET.@)
2054 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2055 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2056 DWORD dwFieldControl)
2058 char *url;
2059 BOOL ret;
2061 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2062 return FALSE;
2064 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2065 heap_free(url);
2066 return ret;
2069 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2071 urlcache_header *header;
2072 struct hash_entry *hash_entry;
2073 entry_url *url_entry;
2074 cache_container *container;
2075 DWORD error;
2077 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2079 if(!url || !size || (!entry_info && *size)) {
2080 SetLastError(ERROR_INVALID_PARAMETER);
2081 return FALSE;
2084 error = cache_containers_find(url, &container);
2085 if(error != ERROR_SUCCESS) {
2086 SetLastError(error);
2087 return FALSE;
2090 error = cache_container_open_index(container, MIN_BLOCK_NO);
2091 if (error != ERROR_SUCCESS) {
2092 SetLastError(error);
2093 return FALSE;
2096 if (!(header = cache_container_lock_index(container)))
2097 return FALSE;
2099 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2100 cache_container_unlock_index(container, header);
2101 TRACE("entry %s not found!\n", url);
2102 SetLastError(ERROR_FILE_NOT_FOUND);
2103 return FALSE;
2106 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2107 if(url_entry->header.signature != URL_SIGNATURE) {
2108 cache_container_unlock_index(container, header);
2109 FIXME("Trying to retrieve entry of unknown format %s\n",
2110 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2111 SetLastError(ERROR_FILE_NOT_FOUND);
2112 return FALSE;
2115 if(!url_entry->local_name_off) {
2116 cache_container_unlock_index(container, header);
2117 SetLastError(ERROR_INVALID_DATA);
2118 return FALSE;
2121 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2122 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2123 url_entry->header_info_size));
2125 error = urlcache_copy_entry(container, header, entry_info,
2126 size, url_entry, unicode);
2127 if(error != ERROR_SUCCESS) {
2128 cache_container_unlock_index(container, header);
2129 SetLastError(error);
2130 return FALSE;
2132 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2134 url_entry->hit_rate++;
2135 url_entry->use_count++;
2136 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2137 GetSystemTimeAsFileTime(&url_entry->access_time);
2139 cache_container_unlock_index(container, header);
2141 return TRUE;
2144 /***********************************************************************
2145 * RetrieveUrlCacheEntryFileA (WININET.@)
2148 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2149 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2150 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2152 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2153 lpdwCacheEntryInfoBufferSize, FALSE);
2156 /***********************************************************************
2157 * RetrieveUrlCacheEntryFileW (WININET.@)
2160 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2161 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2162 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2164 char *url;
2165 BOOL ret;
2167 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2168 return FALSE;
2170 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2171 lpdwCacheEntryInfoBufferSize, TRUE);
2172 heap_free(url);
2173 return ret;
2176 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2177 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2179 entry_header *pEntry;
2180 entry_url * pUrlEntry;
2182 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2183 if (pEntry->signature != URL_SIGNATURE)
2185 FIXME("Trying to delete entry of unknown format %s\n",
2186 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2187 SetLastError(ERROR_FILE_NOT_FOUND);
2188 return FALSE;
2191 pUrlEntry = (entry_url *)pEntry;
2192 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2194 TRACE("Trying to delete locked entry\n");
2195 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2196 SetLastError(ERROR_SHARING_VIOLATION);
2197 return FALSE;
2200 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2202 urlcache_entry_free(pHeader, pEntry);
2204 else
2206 /* Add entry to leaked files list */
2207 pUrlEntry->header.signature = LEAK_SIGNATURE;
2208 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2209 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2212 urlcache_hash_entry_delete(pHashEntry);
2213 return TRUE;
2216 static HANDLE free_cache_running;
2217 static HANDLE dll_unload_event;
2218 static DWORD WINAPI handle_full_cache_worker(void *param)
2220 FreeUrlCacheSpaceW(NULL, 20, 0);
2221 ReleaseSemaphore(free_cache_running, 1, NULL);
2222 return 0;
2225 static void handle_full_cache(void)
2227 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2228 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2229 ReleaseSemaphore(free_cache_running, 1, NULL);
2233 /* Enumerates entries in cache, allows cache unlocking between calls. */
2234 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2235 struct hash_entry **hash_entry, entry_header **entry)
2237 entry_hash_table *hashtable_entry;
2239 *hash_entry = NULL;
2240 *entry = NULL;
2242 if(!*hash_table_off) {
2243 *hash_table_off = header->hash_table_off;
2244 *hash_table_entry = 0;
2246 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2247 }else {
2248 if(*hash_table_off >= header->size) {
2249 *hash_table_off = 0;
2250 return FALSE;
2253 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2256 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2257 *hash_table_off = 0;
2258 return FALSE;
2261 while(1) {
2262 if(*hash_table_entry >= HASHTABLE_SIZE) {
2263 *hash_table_off = hashtable_entry->next;
2264 if(!*hash_table_off) {
2265 *hash_table_off = 0;
2266 return FALSE;
2269 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2270 *hash_table_entry = 0;
2273 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2274 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2275 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2276 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2277 (*hash_table_entry)++;
2278 return TRUE;
2281 (*hash_table_entry)++;
2284 *hash_table_off = 0;
2285 return FALSE;
2288 /* Rates an urlcache entry to determine if it can be deleted.
2290 * Score 0 means that entry can safely be removed, the bigger rating
2291 * the smaller chance of entry being removed.
2292 * DWORD_MAX means that entry can't be deleted at all.
2294 * Rating system is currently not fully compatible with native implementation.
2296 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2298 ULARGE_INTEGER time, access_time;
2299 DWORD rating;
2301 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2302 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2304 time.u.LowPart = cur_time->dwLowDateTime;
2305 time.u.HighPart = cur_time->dwHighDateTime;
2307 /* Don't touch entries that were added less than 10 minutes ago */
2308 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2309 return -1;
2311 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2312 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2313 return -1;
2315 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2316 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2318 if(url_entry->hit_rate > 100)
2319 rating += 100;
2320 else
2321 rating += url_entry->hit_rate;
2323 return rating;
2326 static int dword_cmp(const void *p1, const void *p2)
2328 return *(const DWORD*)p1 - *(const DWORD*)p2;
2331 /***********************************************************************
2332 * FreeUrlCacheSpaceW (WININET.@)
2334 * Frees up some cache.
2336 * PARAMETERS
2337 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2338 * size [I] Percentage of the cache that should be free.
2339 * filter [I] Which entries can't be deleted (CacheEntryType)
2341 * RETURNS
2342 * TRUE success. FALSE failure.
2344 * IMPLEMENTATION
2345 * This implementation just retrieves the path of the cache directory, and
2346 * deletes its contents from the filesystem. The correct approach would
2347 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2349 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2351 cache_container *container;
2352 DWORD path_len, err;
2354 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2356 if(size<1 || size>100) {
2357 SetLastError(ERROR_INVALID_PARAMETER);
2358 return FALSE;
2361 if(cache_path) {
2362 path_len = strlenW(cache_path);
2363 if(cache_path[path_len-1] == '\\')
2364 path_len--;
2365 }else {
2366 path_len = 0;
2369 if(size==100 && !filter) {
2370 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2372 /* When cache_path==NULL only clean Temporary Internet Files */
2373 if((!path_len && container->cache_prefix[0]==0) ||
2374 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2375 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2377 BOOL ret_del;
2379 WaitForSingleObject(container->mutex, INFINITE);
2381 /* unlock, delete, recreate and lock cache */
2382 cache_container_close_index(container);
2383 ret_del = cache_container_delete_dir(container->path);
2384 err = cache_container_open_index(container, MIN_BLOCK_NO);
2386 ReleaseMutex(container->mutex);
2387 if(!ret_del || (err != ERROR_SUCCESS))
2388 return FALSE;
2392 return TRUE;
2395 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2397 urlcache_header *header;
2398 struct hash_entry *hash_entry;
2399 entry_header *entry;
2400 entry_url *url_entry;
2401 ULONGLONG desired_size, cur_size;
2402 DWORD delete_factor, hash_table_off, hash_table_entry;
2403 DWORD rate[100], rate_no;
2404 FILETIME cur_time;
2406 if((path_len || container->cache_prefix[0]!=0) &&
2407 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2408 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2409 continue;
2411 err = cache_container_open_index(container, MIN_BLOCK_NO);
2412 if(err != ERROR_SUCCESS)
2413 continue;
2415 header = cache_container_lock_index(container);
2416 if(!header)
2417 continue;
2419 urlcache_clean_leaked_entries(container, header);
2421 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2422 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2423 if(cur_size <= desired_size)
2424 delete_factor = 0;
2425 else
2426 delete_factor = (cur_size-desired_size)*100/cur_size;
2428 if(!delete_factor) {
2429 cache_container_unlock_index(container, header);
2430 continue;
2433 hash_table_off = 0;
2434 hash_table_entry = 0;
2435 rate_no = 0;
2436 GetSystemTimeAsFileTime(&cur_time);
2437 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2438 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2439 if(entry->signature != URL_SIGNATURE) {
2440 WARN("only url entries are currently supported\n");
2441 continue;
2444 url_entry = (entry_url*)entry;
2445 if(url_entry->cache_entry_type & filter)
2446 continue;
2448 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2449 if(rate[rate_no] != -1)
2450 rate_no++;
2453 if(!rate_no) {
2454 TRACE("nothing to delete\n");
2455 cache_container_unlock_index(container, header);
2456 continue;
2459 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2461 delete_factor = delete_factor*rate_no/100;
2462 delete_factor = rate[delete_factor];
2463 TRACE("deleting files with rating %d or less\n", delete_factor);
2465 hash_table_off = 0;
2466 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2467 if(entry->signature != URL_SIGNATURE)
2468 continue;
2470 url_entry = (entry_url*)entry;
2471 if(url_entry->cache_entry_type & filter)
2472 continue;
2474 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2475 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2476 urlcache_entry_delete(container, header, hash_entry);
2478 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2479 break;
2481 /* Allow other threads to use cache while cleaning */
2482 cache_container_unlock_index(container, header);
2483 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2484 TRACE("got dll_unload_event - finishing\n");
2485 return TRUE;
2487 Sleep(0);
2488 header = cache_container_lock_index(container);
2492 TRACE("cache size after cleaning 0x%s/0x%s\n",
2493 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2494 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2495 cache_container_unlock_index(container, header);
2498 return TRUE;
2501 /***********************************************************************
2502 * FreeUrlCacheSpaceA (WININET.@)
2504 * See FreeUrlCacheSpaceW.
2506 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2508 BOOL ret = FALSE;
2509 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2510 if (lpszCachePath == NULL || path != NULL)
2511 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2512 heap_free(path);
2513 return ret;
2516 /***********************************************************************
2517 * UnlockUrlCacheEntryFileA (WININET.@)
2520 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2522 urlcache_header *pHeader;
2523 struct hash_entry *pHashEntry;
2524 entry_header *pEntry;
2525 entry_url * pUrlEntry;
2526 cache_container *pContainer;
2527 DWORD error;
2529 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2531 if (dwReserved)
2533 ERR("dwReserved != 0\n");
2534 SetLastError(ERROR_INVALID_PARAMETER);
2535 return FALSE;
2538 error = cache_containers_find(lpszUrlName, &pContainer);
2539 if (error != ERROR_SUCCESS)
2541 SetLastError(error);
2542 return FALSE;
2545 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2546 if (error != ERROR_SUCCESS)
2548 SetLastError(error);
2549 return FALSE;
2552 if (!(pHeader = cache_container_lock_index(pContainer)))
2553 return FALSE;
2555 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2557 cache_container_unlock_index(pContainer, pHeader);
2558 TRACE("entry %s not found!\n", lpszUrlName);
2559 SetLastError(ERROR_FILE_NOT_FOUND);
2560 return FALSE;
2563 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2564 if (pEntry->signature != URL_SIGNATURE)
2566 cache_container_unlock_index(pContainer, pHeader);
2567 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2568 SetLastError(ERROR_FILE_NOT_FOUND);
2569 return FALSE;
2572 pUrlEntry = (entry_url *)pEntry;
2574 if (pUrlEntry->use_count == 0)
2576 cache_container_unlock_index(pContainer, pHeader);
2577 return FALSE;
2579 pUrlEntry->use_count--;
2580 if (!pUrlEntry->use_count)
2582 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2583 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2584 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2587 cache_container_unlock_index(pContainer, pHeader);
2589 return TRUE;
2592 /***********************************************************************
2593 * UnlockUrlCacheEntryFileW (WININET.@)
2596 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2598 char *url;
2599 BOOL ret;
2601 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2602 return FALSE;
2604 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2605 heap_free(url);
2606 return ret;
2609 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2611 cache_container *container;
2612 urlcache_header *header;
2613 char file_name[MAX_PATH];
2614 WCHAR extW[MAX_PATH];
2615 BYTE cache_dir;
2616 LONG full_path_len;
2617 BOOL generate_name = FALSE;
2618 DWORD error;
2619 HANDLE file;
2620 FILETIME ft;
2621 URL_COMPONENTSA uc;
2622 int i;
2624 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2626 memset(&uc, 0, sizeof(uc));
2627 uc.dwStructSize = sizeof(uc);
2628 uc.dwUrlPathLength = 1;
2629 uc.dwExtraInfoLength = 1;
2630 if(!InternetCrackUrlA(url, 0, 0, &uc))
2631 uc.dwUrlPathLength = 0;
2633 if(!uc.dwUrlPathLength) {
2634 file_name[0] = 0;
2635 }else {
2636 char *p, *e;
2638 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2639 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2640 p--;
2641 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2642 e = p-1;
2643 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2644 p--;
2647 memcpy(file_name, p, e-p);
2648 file_name[e-p] = 0;
2650 for(p=file_name; *p; p++) {
2651 switch(*p) {
2652 case '<': case '>':
2653 case ':': case '"':
2654 case '|': case '?':
2655 case '*':
2656 *p = '_'; break;
2657 default: break;
2662 if(!file_name[0])
2663 generate_name = TRUE;
2665 error = cache_containers_find(url, &container);
2666 if(error != ERROR_SUCCESS) {
2667 SetLastError(error);
2668 return FALSE;
2671 error = cache_container_open_index(container, MIN_BLOCK_NO);
2672 if(error != ERROR_SUCCESS) {
2673 SetLastError(error);
2674 return FALSE;
2677 if(!(header = cache_container_lock_index(container)))
2678 return FALSE;
2680 if(header->dirs_no)
2681 cache_dir = (BYTE)(rand() % header->dirs_no);
2682 else
2683 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2685 full_path_len = MAX_PATH * sizeof(WCHAR);
2686 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len)) {
2687 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2688 debugstr_a(file_name), full_path_len);
2689 cache_container_unlock_index(container, header);
2690 return FALSE;
2692 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2694 cache_container_unlock_index(container, header);
2696 if(ext) {
2697 WCHAR *p;
2699 extW[0] = '.';
2700 MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2702 for(p=extW; *p; p++) {
2703 switch(*p) {
2704 case '<': case '>':
2705 case ':': case '"':
2706 case '|': case '?':
2707 case '*':
2708 *p = '_'; break;
2709 default: break;
2712 if(p[-1]==' ' || p[-1]=='.')
2713 p[-1] = '_';
2714 }else {
2715 extW[0] = '\0';
2718 for(i=0; i<255 && !generate_name; i++) {
2719 static const WCHAR format[] = {'[','%','u',']','%','s',0};
2721 wsprintfW(full_path+full_path_len, format, i, extW);
2723 TRACE("Trying: %s\n", debugstr_w(full_path));
2724 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2725 if(file != INVALID_HANDLE_VALUE) {
2726 CloseHandle(file);
2727 return TRUE;
2731 /* Try to generate random name */
2732 GetSystemTimeAsFileTime(&ft);
2733 strcpyW(full_path+full_path_len+8, extW);
2735 for(i=0; i<255; i++) {
2736 int j;
2737 ULONGLONG n = ft.dwHighDateTime;
2738 n <<= 32;
2739 n += ft.dwLowDateTime;
2740 n ^= (ULONGLONG)i<<48;
2742 for(j=0; j<8; j++) {
2743 int r = (n % 36);
2744 n /= 37;
2745 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2748 TRACE("Trying: %s\n", debugstr_w(full_path));
2749 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2750 if(file != INVALID_HANDLE_VALUE) {
2751 CloseHandle(file);
2752 return TRUE;
2756 WARN("Could not find a unique filename\n");
2757 return FALSE;
2760 /***********************************************************************
2761 * CreateUrlCacheEntryA (WININET.@)
2764 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2765 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2767 WCHAR file_name[MAX_PATH];
2769 if(dwReserved)
2770 FIXME("dwReserved 0x%08x\n", dwReserved);
2772 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2773 return FALSE;
2775 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2776 return FALSE;
2777 return TRUE;
2779 /***********************************************************************
2780 * CreateUrlCacheEntryW (WININET.@)
2783 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2784 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2786 char *url, *ext = NULL;
2787 BOOL ret;
2789 if(dwReserved)
2790 FIXME("dwReserved 0x%08x\n", dwReserved);
2792 if(lpszFileExtension) {
2793 ext = heap_strdupWtoUTF8(lpszFileExtension);
2794 if(!ext)
2795 return FALSE;
2798 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2799 heap_free(ext);
2800 return FALSE;
2803 ret = urlcache_entry_create(url, ext, lpszFileName);
2804 heap_free(ext);
2805 heap_free(url);
2806 return ret;
2809 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2810 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2811 BYTE *header_info, DWORD header_size, const char *file_ext,
2812 const char *original_url)
2814 cache_container *container;
2815 urlcache_header *header;
2816 struct hash_entry *hash_entry;
2817 entry_header *entry;
2818 entry_url *url_entry;
2819 DWORD url_entry_offset;
2820 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2821 DWORD file_name_off = 0;
2822 DWORD header_info_off = 0;
2823 DWORD file_ext_off = 0;
2824 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2825 LARGE_INTEGER file_size;
2826 BYTE dir_id;
2827 char file_name_no_container[MAX_PATH];
2828 char *local_file_name = 0;
2829 DWORD hit_rate = 0;
2830 DWORD exempt_delta = 0;
2831 DWORD error;
2833 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2834 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2836 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2837 SetLastError(ERROR_INVALID_PARAMETER);
2838 return FALSE;
2840 if(original_url)
2841 WARN(": original_url ignored\n");
2843 memset(&file_attr, 0, sizeof(file_attr));
2844 if(file_name) {
2845 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2846 return FALSE;
2848 file_size.u.LowPart = file_attr.nFileSizeLow;
2849 file_size.u.HighPart = file_attr.nFileSizeHigh;
2851 error = cache_containers_find(url, &container);
2852 if(error != ERROR_SUCCESS) {
2853 SetLastError(error);
2854 return FALSE;
2857 error = cache_container_open_index(container, MIN_BLOCK_NO);
2858 if(error != ERROR_SUCCESS) {
2859 SetLastError(error);
2860 return FALSE;
2863 if(!(header = cache_container_lock_index(container)))
2864 return FALSE;
2866 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2867 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2869 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2870 TRACE("Trying to overwrite locked entry\n");
2871 cache_container_unlock_index(container, header);
2872 SetLastError(ERROR_SHARING_VIOLATION);
2873 return FALSE;
2876 hit_rate = url_entry->hit_rate;
2877 exempt_delta = url_entry->exempt_delta;
2878 urlcache_entry_delete(container, header, hash_entry);
2881 if(header->dirs_no)
2882 dir_id = 0;
2883 else
2884 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2886 if(file_name) {
2887 BOOL bFound = FALSE;
2889 if(strncmpW(file_name, container->path, lstrlenW(container->path))) {
2890 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2891 cache_container_unlock_index(container, header);
2892 SetLastError(ERROR_INVALID_PARAMETER);
2893 return FALSE;
2896 /* skip container path prefix */
2897 file_name += lstrlenW(container->path);
2899 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2900 local_file_name = file_name_no_container;
2902 if(header->dirs_no) {
2903 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2904 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2905 bFound = TRUE;
2906 break;
2910 if(!bFound) {
2911 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2912 cache_container_unlock_index(container, header);
2913 SetLastError(ERROR_INVALID_PARAMETER);
2914 return FALSE;
2917 file_name += DIR_LENGTH + 1;
2918 local_file_name += DIR_LENGTH + 1;
2922 size = DWORD_ALIGN(size + strlen(url) + 1);
2923 if(file_name) {
2924 file_name_off = size;
2925 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2927 if(header_info && header_size) {
2928 header_info_off = size;
2929 size = DWORD_ALIGN(size + header_size);
2931 if(file_ext && (file_ext_off = strlen(file_ext))) {
2932 DWORD len = file_ext_off;
2934 file_ext_off = size;
2935 size = DWORD_ALIGN(size + len + 1);
2938 /* round up to next block */
2939 if(size % BLOCKSIZE) {
2940 size -= size % BLOCKSIZE;
2941 size += BLOCKSIZE;
2944 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2945 while(error == ERROR_HANDLE_DISK_FULL) {
2946 error = cache_container_clean_index(container, &header);
2947 if(error == ERROR_SUCCESS)
2948 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2950 if(error != ERROR_SUCCESS) {
2951 cache_container_unlock_index(container, header);
2952 SetLastError(error);
2953 return FALSE;
2956 /* FindFirstFreeEntry fills in blocks used */
2957 url_entry = (entry_url *)entry;
2958 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2959 url_entry->header.signature = URL_SIGNATURE;
2960 url_entry->cache_dir = dir_id;
2961 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2962 url_entry->header_info_size = header_size;
2963 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2964 /* Sticky entries have a default exempt time of one day */
2965 exempt_delta = 86400;
2967 url_entry->exempt_delta = exempt_delta;
2968 url_entry->hit_rate = hit_rate+1;
2969 url_entry->file_extension_off = file_ext_off;
2970 url_entry->header_info_off = header_info_off;
2971 url_entry->local_name_off = file_name_off;
2972 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2973 url_entry->size.QuadPart = file_size.QuadPart;
2974 url_entry->use_count = 0;
2975 GetSystemTimeAsFileTime(&url_entry->access_time);
2976 url_entry->modification_time = modify_time;
2977 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2978 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2979 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2981 /*** Unknowns ***/
2982 url_entry->unk1 = 0;
2983 url_entry->unk2 = 0;
2984 url_entry->unk3 = 0x60;
2985 url_entry->unk4 = 0;
2986 url_entry->unk5 = 0x1010;
2987 url_entry->unk7 = 0;
2988 url_entry->unk8 = 0;
2991 strcpy((LPSTR)url_entry + url_entry->url_off, url);
2992 if(file_name_off)
2993 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
2994 if(header_info_off)
2995 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
2996 if(file_ext_off)
2997 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
2999 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3000 while(error == ERROR_HANDLE_DISK_FULL) {
3001 error = cache_container_clean_index(container, &header);
3002 if(error == ERROR_SUCCESS) {
3003 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3004 error = urlcache_hash_entry_create(header, url,
3005 url_entry_offset, HASHTABLE_URL);
3008 if(error != ERROR_SUCCESS) {
3009 urlcache_entry_free(header, &url_entry->header);
3010 cache_container_unlock_index(container, header);
3011 SetLastError(error);
3012 return FALSE;
3015 if(url_entry->cache_dir < header->dirs_no)
3016 header->directory_data[url_entry->cache_dir].files_no++;
3017 if(entry_type & STICKY_CACHE_ENTRY)
3018 header->exempt_usage.QuadPart += file_size.QuadPart;
3019 else
3020 header->cache_usage.QuadPart += file_size.QuadPart;
3021 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3022 handle_full_cache();
3024 cache_container_unlock_index(container, header);
3025 return TRUE;
3028 /***********************************************************************
3029 * CommitUrlCacheEntryA (WININET.@)
3031 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3032 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3033 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3035 WCHAR *file_name = NULL;
3036 BOOL ret;
3038 if(lpszLocalFileName) {
3039 file_name = heap_strdupAtoW(lpszLocalFileName);
3040 if(!file_name)
3041 return FALSE;
3044 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3045 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3046 heap_free(file_name);
3047 return ret;
3050 /***********************************************************************
3051 * CommitUrlCacheEntryW (WININET.@)
3053 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3054 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3055 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3057 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3058 BOOL ret;
3060 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3061 return FALSE;
3063 if(lpHeaderInfo) {
3064 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3065 if(!header_info) {
3066 heap_free(url);
3067 return FALSE;
3069 dwHeaderSize = strlen(header_info);
3072 if(lpszFileExtension) {
3073 file_ext = heap_strdupWtoA(lpszFileExtension);
3074 if(!file_ext) {
3075 heap_free(url);
3076 heap_free(header_info);
3077 return FALSE;
3081 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3082 heap_free(url);
3083 heap_free(header_info);
3084 heap_free(file_ext);
3085 return FALSE;
3088 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3089 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3090 heap_free(url);
3091 heap_free(header_info);
3092 heap_free(file_ext);
3093 heap_free(original_url);
3094 return ret;
3097 /***********************************************************************
3098 * ReadUrlCacheEntryStream (WININET.@)
3101 BOOL WINAPI ReadUrlCacheEntryStream(
3102 IN HANDLE hUrlCacheStream,
3103 IN DWORD dwLocation,
3104 IN OUT LPVOID lpBuffer,
3105 IN OUT LPDWORD lpdwLen,
3106 IN DWORD dwReserved
3109 /* Get handle to file from 'stream' */
3110 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3112 if (dwReserved != 0)
3114 ERR("dwReserved != 0\n");
3115 SetLastError(ERROR_INVALID_PARAMETER);
3116 return FALSE;
3119 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3121 SetLastError(ERROR_INVALID_HANDLE);
3122 return FALSE;
3125 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3126 return FALSE;
3127 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3130 /***********************************************************************
3131 * RetrieveUrlCacheEntryStreamA (WININET.@)
3134 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3135 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3136 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3138 /* NOTE: this is not the same as the way that the native
3139 * version allocates 'stream' handles. I did it this way
3140 * as it is much easier and no applications should depend
3141 * on this behaviour. (Native version appears to allocate
3142 * indices into a table)
3144 stream_handle *stream;
3145 HANDLE file;
3147 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3148 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3150 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3151 lpdwCacheEntryInfoBufferSize, dwReserved))
3152 return NULL;
3154 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3155 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3156 if(file == INVALID_HANDLE_VALUE) {
3157 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3158 return NULL;
3161 /* allocate handle storage space */
3162 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3163 if(!stream) {
3164 CloseHandle(file);
3165 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3166 SetLastError(ERROR_OUTOFMEMORY);
3167 return NULL;
3170 stream->file = file;
3171 strcpy(stream->url, lpszUrlName);
3172 return stream;
3175 /***********************************************************************
3176 * RetrieveUrlCacheEntryStreamW (WININET.@)
3179 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3180 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3181 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3183 DWORD len;
3184 /* NOTE: this is not the same as the way that the native
3185 * version allocates 'stream' handles. I did it this way
3186 * as it is much easier and no applications should depend
3187 * on this behaviour. (Native version appears to allocate
3188 * indices into a table)
3190 stream_handle *stream;
3191 HANDLE file;
3193 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3194 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3196 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3197 return NULL;
3199 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3200 lpdwCacheEntryInfoBufferSize, dwReserved))
3201 return NULL;
3203 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3204 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3205 if(file == INVALID_HANDLE_VALUE) {
3206 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3207 return NULL;
3210 /* allocate handle storage space */
3211 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3212 if(!stream) {
3213 CloseHandle(file);
3214 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3215 SetLastError(ERROR_OUTOFMEMORY);
3216 return NULL;
3219 stream->file = file;
3220 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3221 CloseHandle(file);
3222 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3223 heap_free(stream);
3224 return NULL;
3226 return stream;
3229 /***********************************************************************
3230 * UnlockUrlCacheEntryStream (WININET.@)
3233 BOOL WINAPI UnlockUrlCacheEntryStream(
3234 IN HANDLE hUrlCacheStream,
3235 IN DWORD dwReserved
3238 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3240 if (dwReserved != 0)
3242 ERR("dwReserved != 0\n");
3243 SetLastError(ERROR_INVALID_PARAMETER);
3244 return FALSE;
3247 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3249 SetLastError(ERROR_INVALID_HANDLE);
3250 return FALSE;
3253 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3254 return FALSE;
3256 CloseHandle(pStream->file);
3257 heap_free(pStream);
3258 return TRUE;
3262 /***********************************************************************
3263 * DeleteUrlCacheEntryA (WININET.@)
3266 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3268 cache_container *pContainer;
3269 urlcache_header *pHeader;
3270 struct hash_entry *pHashEntry;
3271 DWORD error;
3272 BOOL ret;
3274 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3276 error = cache_containers_find(lpszUrlName, &pContainer);
3277 if (error != ERROR_SUCCESS)
3279 SetLastError(error);
3280 return FALSE;
3283 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3284 if (error != ERROR_SUCCESS)
3286 SetLastError(error);
3287 return FALSE;
3290 if (!(pHeader = cache_container_lock_index(pContainer)))
3291 return FALSE;
3293 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3295 cache_container_unlock_index(pContainer, pHeader);
3296 TRACE("entry %s not found!\n", lpszUrlName);
3297 SetLastError(ERROR_FILE_NOT_FOUND);
3298 return FALSE;
3301 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3303 cache_container_unlock_index(pContainer, pHeader);
3305 return ret;
3308 /***********************************************************************
3309 * DeleteUrlCacheEntryW (WININET.@)
3312 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3314 char *url;
3315 BOOL ret;
3317 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3318 return FALSE;
3320 ret = DeleteUrlCacheEntryA(url);
3321 heap_free(url);
3322 return ret;
3325 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3327 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3328 return TRUE;
3331 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3333 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3334 return TRUE;
3337 /***********************************************************************
3338 * CreateCacheContainerA (WININET.@)
3340 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3341 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3343 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3344 d1, d2, d3, d4, d5, d6, d7, d8);
3345 return TRUE;
3348 /***********************************************************************
3349 * CreateCacheContainerW (WININET.@)
3351 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3352 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3354 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3355 d1, d2, d3, d4, d5, d6, d7, d8);
3356 return TRUE;
3359 /***********************************************************************
3360 * FindFirstUrlCacheContainerA (WININET.@)
3362 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3364 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3365 return NULL;
3368 /***********************************************************************
3369 * FindFirstUrlCacheContainerW (WININET.@)
3371 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3373 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3374 return NULL;
3377 /***********************************************************************
3378 * FindNextUrlCacheContainerA (WININET.@)
3380 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3382 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3383 return FALSE;
3386 /***********************************************************************
3387 * FindNextUrlCacheContainerW (WININET.@)
3389 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3391 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3392 return FALSE;
3395 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3396 LPCSTR lpszUrlSearchPattern,
3397 DWORD dwFlags,
3398 DWORD dwFilter,
3399 GROUPID GroupId,
3400 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3401 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3402 LPVOID lpReserved,
3403 LPDWORD pcbReserved2,
3404 LPVOID lpReserved3
3407 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3408 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3409 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3410 SetLastError(ERROR_FILE_NOT_FOUND);
3411 return NULL;
3414 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3415 LPCWSTR lpszUrlSearchPattern,
3416 DWORD dwFlags,
3417 DWORD dwFilter,
3418 GROUPID GroupId,
3419 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3420 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3421 LPVOID lpReserved,
3422 LPDWORD pcbReserved2,
3423 LPVOID lpReserved3
3426 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3427 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3428 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3429 SetLastError(ERROR_FILE_NOT_FOUND);
3430 return NULL;
3433 /***********************************************************************
3434 * FindFirstUrlCacheEntryA (WININET.@)
3437 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3438 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3440 find_handle *pEntryHandle;
3442 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3444 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3445 if (!pEntryHandle)
3446 return NULL;
3448 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3449 if (lpszUrlSearchPattern)
3451 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3452 if (!pEntryHandle->url_search_pattern)
3454 heap_free(pEntryHandle);
3455 return NULL;
3458 else
3459 pEntryHandle->url_search_pattern = NULL;
3460 pEntryHandle->container_idx = 0;
3461 pEntryHandle->hash_table_idx = 0;
3462 pEntryHandle->hash_entry_idx = 0;
3464 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3466 heap_free(pEntryHandle);
3467 return NULL;
3469 return pEntryHandle;
3472 /***********************************************************************
3473 * FindFirstUrlCacheEntryW (WININET.@)
3476 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3477 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3479 find_handle *pEntryHandle;
3481 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3483 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3484 if (!pEntryHandle)
3485 return NULL;
3487 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3488 if (lpszUrlSearchPattern)
3490 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3491 if (!pEntryHandle->url_search_pattern)
3493 heap_free(pEntryHandle);
3494 return NULL;
3497 else
3498 pEntryHandle->url_search_pattern = NULL;
3499 pEntryHandle->container_idx = 0;
3500 pEntryHandle->hash_table_idx = 0;
3501 pEntryHandle->hash_entry_idx = 0;
3503 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3505 heap_free(pEntryHandle);
3506 return NULL;
3508 return pEntryHandle;
3511 static BOOL urlcache_find_next_entry(
3512 HANDLE hEnumHandle,
3513 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3514 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3515 BOOL unicode)
3517 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3518 cache_container *pContainer;
3520 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3522 SetLastError(ERROR_INVALID_HANDLE);
3523 return FALSE;
3526 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3527 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3529 urlcache_header *pHeader;
3530 entry_hash_table *pHashTableEntry;
3531 DWORD error;
3533 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3534 if (error != ERROR_SUCCESS)
3536 SetLastError(error);
3537 return FALSE;
3540 if (!(pHeader = cache_container_lock_index(pContainer)))
3541 return FALSE;
3543 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3544 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3546 const struct hash_entry *pHashEntry = NULL;
3547 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3548 pEntryHandle->hash_entry_idx++)
3550 const entry_url *pUrlEntry;
3551 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3553 if (pEntry->signature != URL_SIGNATURE)
3554 continue;
3556 pUrlEntry = (const entry_url *)pEntry;
3557 TRACE("Found URL: %s\n",
3558 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3559 TRACE("Header info: %s\n",
3560 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3561 pUrlEntry->header_info_size));
3563 error = urlcache_copy_entry(
3564 pContainer,
3565 pHeader,
3566 lpNextCacheEntryInfo,
3567 lpdwNextCacheEntryInfoBufferSize,
3568 pUrlEntry,
3569 unicode);
3570 if (error != ERROR_SUCCESS)
3572 cache_container_unlock_index(pContainer, pHeader);
3573 SetLastError(error);
3574 return FALSE;
3576 if(pUrlEntry->local_name_off)
3577 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3579 /* increment the current index so that next time the function
3580 * is called the next entry is returned */
3581 pEntryHandle->hash_entry_idx++;
3582 cache_container_unlock_index(pContainer, pHeader);
3583 return TRUE;
3587 cache_container_unlock_index(pContainer, pHeader);
3590 SetLastError(ERROR_NO_MORE_ITEMS);
3591 return FALSE;
3594 /***********************************************************************
3595 * FindNextUrlCacheEntryA (WININET.@)
3597 BOOL WINAPI FindNextUrlCacheEntryA(
3598 HANDLE hEnumHandle,
3599 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3600 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3602 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3604 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3605 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3608 /***********************************************************************
3609 * FindNextUrlCacheEntryW (WININET.@)
3611 BOOL WINAPI FindNextUrlCacheEntryW(
3612 HANDLE hEnumHandle,
3613 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3614 LPDWORD lpdwNextCacheEntryInfoBufferSize
3617 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3619 return urlcache_find_next_entry(hEnumHandle,
3620 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3621 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3624 /***********************************************************************
3625 * FindCloseUrlCache (WININET.@)
3627 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3629 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3631 TRACE("(%p)\n", hEnumHandle);
3633 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3635 SetLastError(ERROR_INVALID_HANDLE);
3636 return FALSE;
3639 pEntryHandle->magic = 0;
3640 heap_free(pEntryHandle->url_search_pattern);
3641 heap_free(pEntryHandle);
3642 return TRUE;
3645 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3646 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3648 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3649 dwSearchCondition, lpGroupId, lpReserved);
3650 return NULL;
3653 BOOL WINAPI FindNextUrlCacheEntryExA(
3654 HANDLE hEnumHandle,
3655 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3656 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3657 LPVOID lpReserved,
3658 LPDWORD pcbReserved2,
3659 LPVOID lpReserved3
3662 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3663 lpReserved, pcbReserved2, lpReserved3);
3664 return FALSE;
3667 BOOL WINAPI FindNextUrlCacheEntryExW(
3668 HANDLE hEnumHandle,
3669 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3670 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3671 LPVOID lpReserved,
3672 LPDWORD pcbReserved2,
3673 LPVOID lpReserved3
3676 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3677 lpReserved, pcbReserved2, lpReserved3);
3678 return FALSE;
3681 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3683 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3684 return FALSE;
3687 /***********************************************************************
3688 * CreateUrlCacheGroup (WININET.@)
3691 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3693 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3694 return FALSE;
3697 /***********************************************************************
3698 * DeleteUrlCacheGroup (WININET.@)
3701 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3703 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3704 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3705 return FALSE;
3708 /***********************************************************************
3709 * DeleteWpadCacheForNetworks (WININET.@)
3710 * Undocumented, added in IE8
3712 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3714 FIXME("(%d) stub\n", unk1);
3715 return FALSE;
3718 /***********************************************************************
3719 * SetUrlCacheEntryGroupA (WININET.@)
3722 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3723 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3724 LPVOID lpReserved)
3726 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3727 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3728 pbGroupAttributes, cbGroupAttributes, lpReserved);
3729 SetLastError(ERROR_FILE_NOT_FOUND);
3730 return FALSE;
3733 /***********************************************************************
3734 * SetUrlCacheEntryGroupW (WININET.@)
3737 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3738 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3739 LPVOID lpReserved)
3741 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3742 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3743 pbGroupAttributes, cbGroupAttributes, lpReserved);
3744 SetLastError(ERROR_FILE_NOT_FOUND);
3745 return FALSE;
3748 /***********************************************************************
3749 * GetUrlCacheConfigInfoW (WININET.@)
3751 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3753 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3754 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3755 return FALSE;
3758 /***********************************************************************
3759 * GetUrlCacheConfigInfoA (WININET.@)
3761 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3763 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3764 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3765 return FALSE;
3768 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3769 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3770 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3772 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3773 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3774 lpdwGroupInfo, lpReserved);
3775 return FALSE;
3778 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3779 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3780 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3782 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3783 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3784 lpdwGroupInfo, lpReserved);
3785 return FALSE;
3788 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3789 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3791 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3792 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3793 return TRUE;
3796 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3797 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3799 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3800 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3801 return TRUE;
3804 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3806 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3807 return TRUE;
3810 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3812 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3813 return TRUE;
3816 /***********************************************************************
3817 * DeleteIE3Cache (WININET.@)
3819 * Deletes the files used by the IE3 URL caching system.
3821 * PARAMS
3822 * hWnd [I] A dummy window.
3823 * hInst [I] Instance of process calling the function.
3824 * lpszCmdLine [I] Options used by function.
3825 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3827 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3829 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3830 return 0;
3833 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3834 FILETIME *pftLastModified)
3836 BOOL ret;
3837 FILETIME now, expired;
3839 *pftLastModified = pUrlEntry->modification_time;
3840 GetSystemTimeAsFileTime(&now);
3841 dos_date_time_to_file_time(pUrlEntry->expire_date,
3842 pUrlEntry->expire_time, &expired);
3843 /* If the expired time is 0, it's interpreted as not expired */
3844 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3845 ret = FALSE;
3846 else
3847 ret = CompareFileTime(&expired, &now) < 0;
3848 return ret;
3851 /***********************************************************************
3852 * IsUrlCacheEntryExpiredA (WININET.@)
3854 * PARAMS
3855 * url [I] Url
3856 * dwFlags [I] Unknown
3857 * pftLastModified [O] Last modified time
3859 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3861 urlcache_header *pHeader;
3862 struct hash_entry *pHashEntry;
3863 const entry_header *pEntry;
3864 const entry_url * pUrlEntry;
3865 cache_container *pContainer;
3866 BOOL expired;
3868 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3870 if (!url || !pftLastModified)
3871 return TRUE;
3872 if (dwFlags)
3873 FIXME("unknown flags 0x%08x\n", dwFlags);
3875 /* Any error implies that the URL is expired, i.e. not in the cache */
3876 if (cache_containers_find(url, &pContainer))
3878 memset(pftLastModified, 0, sizeof(*pftLastModified));
3879 return TRUE;
3882 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3884 memset(pftLastModified, 0, sizeof(*pftLastModified));
3885 return TRUE;
3888 if (!(pHeader = cache_container_lock_index(pContainer)))
3890 memset(pftLastModified, 0, sizeof(*pftLastModified));
3891 return TRUE;
3894 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3896 cache_container_unlock_index(pContainer, pHeader);
3897 memset(pftLastModified, 0, sizeof(*pftLastModified));
3898 TRACE("entry %s not found!\n", url);
3899 return TRUE;
3902 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3903 if (pEntry->signature != URL_SIGNATURE)
3905 cache_container_unlock_index(pContainer, pHeader);
3906 memset(pftLastModified, 0, sizeof(*pftLastModified));
3907 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3908 return TRUE;
3911 pUrlEntry = (const entry_url *)pEntry;
3912 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
3914 cache_container_unlock_index(pContainer, pHeader);
3916 return expired;
3919 /***********************************************************************
3920 * IsUrlCacheEntryExpiredW (WININET.@)
3922 * PARAMS
3923 * url [I] Url
3924 * dwFlags [I] Unknown
3925 * pftLastModified [O] Last modified time
3927 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3929 char *encoded_url;
3930 BOOL ret;
3932 if(!urlcache_encode_url_alloc(url, &encoded_url))
3933 return FALSE;
3935 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
3936 heap_free(encoded_url);
3937 return ret;
3940 /***********************************************************************
3941 * GetDiskInfoA (WININET.@)
3943 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3945 BOOL ret;
3946 ULARGE_INTEGER bytes_free, bytes_total;
3948 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3950 if (!path)
3952 SetLastError(ERROR_INVALID_PARAMETER);
3953 return FALSE;
3956 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3958 if (cluster_size) *cluster_size = 1;
3959 if (free) *free = bytes_free.QuadPart;
3960 if (total) *total = bytes_total.QuadPart;
3962 return ret;
3965 /***********************************************************************
3966 * RegisterUrlCacheNotification (WININET.@)
3968 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3970 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3971 return 0;
3974 /***********************************************************************
3975 * IncrementUrlCacheHeaderData (WININET.@)
3977 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3979 FIXME("(%u, %p)\n", index, data);
3980 return FALSE;
3983 /***********************************************************************
3984 * RunOnceUrlCache (WININET.@)
3987 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
3989 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
3990 return 0;
3993 BOOL init_urlcache(void)
3995 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
3996 if(!dll_unload_event)
3997 return FALSE;
3999 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4000 if(!free_cache_running) {
4001 CloseHandle(dll_unload_event);
4002 return FALSE;
4005 cache_containers_init();
4006 return TRUE;
4009 void free_urlcache(void)
4011 SetEvent(dll_unload_event);
4012 WaitForSingleObject(free_cache_running, INFINITE);
4013 ReleaseSemaphore(free_cache_running, 1, NULL);
4014 CloseHandle(free_cache_running);
4015 CloseHandle(dll_unload_event);
4017 cache_containers_free();
4020 /***********************************************************************
4021 * LoadUrlCacheContent (WININET.@)
4023 BOOL WINAPI LoadUrlCacheContent(void)
4025 FIXME("stub!\n");
4026 return FALSE;