wininet/tests: Add tests for a prematurely closed connection.
[wine] / dlls / wininet / urlcache.c
1 /*
2  * Wininet - Url Cache functions
3  *
4  * Copyright 2001,2002 CodeWeavers
5  * Copyright 2003-2008 Robert Shearman
6  *
7  * Eric Kohl
8  * Aric Stewart
9  *
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.
14  *
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.
19  *
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
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30
31 #if defined(__MINGW32__) || defined (_MSC_VER)
32 #include <ws2tcpip.h>
33 #endif
34
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #include <time.h>
44
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wininet.h"
49 #include "winineti.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "shlwapi.h"
53 #include "shlobj.h"
54 #include "shellapi.h"
55
56 #include "internet.h"
57
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
60
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
62
63 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
64 static const char urlcache_ver[] = "0.2012001";
65
66 #ifndef CHAR_BIT
67 #define CHAR_BIT    (8 * sizeof(CHAR))
68 #endif
69
70 #define ENTRY_START_OFFSET      0x4000
71 #define DIR_LENGTH              8
72 #define MAX_DIR_NO              0x20
73 #define BLOCKSIZE               128
74 #define HASHTABLE_SIZE          448
75 #define HASHTABLE_NUM_ENTRIES   64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
76 #define HASHTABLE_BLOCKSIZE     (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
77 #define ALLOCATION_TABLE_OFFSET 0x250
78 #define ALLOCATION_TABLE_SIZE   (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
79 #define MIN_BLOCK_NO            0x80
80 #define MAX_BLOCK_NO            (ALLOCATION_TABLE_SIZE * CHAR_BIT)
81 #define FILE_SIZE(blocks)       ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
82
83 #define HASHTABLE_URL           0
84 #define HASHTABLE_DEL           1
85 #define HASHTABLE_LOCK          2
86 #define HASHTABLE_FREE          3
87 #define HASHTABLE_REDR          5
88 #define HASHTABLE_FLAG_BITS     6
89
90 #define PENDING_DELETE_CACHE_ENTRY  0x00400000
91 #define INSTALLED_CACHE_ENTRY       0x10000000
92 #define GET_INSTALLED_ENTRY         0x200
93 #define CACHE_CONTAINER_NO_SUBDIR   0xFE
94
95 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
96
97 #define FILETIME_SECOND 10000000
98
99 #define DWORD_SIG(a,b,c,d)  (a | (b << 8) | (c << 16) | (d << 24))
100 #define URL_SIGNATURE   DWORD_SIG('U','R','L',' ')
101 #define REDR_SIGNATURE  DWORD_SIG('R','E','D','R')
102 #define LEAK_SIGNATURE  DWORD_SIG('L','E','A','K')
103 #define HASH_SIGNATURE  DWORD_SIG('H','A','S','H')
104
105 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
106
107 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
108
109 typedef struct
110 {
111     DWORD signature;
112     DWORD blocks_used; /* number of 128byte blocks used by this entry */
113 } entry_header;
114
115 typedef struct
116 {
117     entry_header header;
118     FILETIME modification_time;
119     FILETIME access_time;
120     WORD expire_date; /* expire date in dos format */
121     WORD expire_time; /* expire time in dos format */
122     DWORD unk1; /* usually zero */
123     ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
124     DWORD unk2; /* usually zero */
125     DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
126     DWORD unk3; /* usually 0x60 */
127     DWORD url_off; /* offset of start of url from start of entry */
128     BYTE cache_dir; /* index of cache directory this url is stored in */
129     BYTE unk4; /* usually zero */
130     WORD unk5; /* usually 0x1010 */
131     DWORD local_name_off; /* offset of start of local filename from start of entry */
132     DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
133     DWORD header_info_off; /* offset of start of header info from start of entry */
134     DWORD header_info_size;
135     DWORD file_extension_off; /* offset of start of file extension from start of entry */
136     WORD sync_date; /* last sync date in dos format */
137     WORD sync_time; /* last sync time in dos format */
138     DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
139     DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
140     WORD write_date;
141     WORD write_time;
142     DWORD unk7; /* usually zero */
143     DWORD unk8; /* usually zero */
144     /* packing to dword align start of next field */
145     /* CHAR szSourceUrlName[]; (url) */
146     /* packing to dword align start of next field */
147     /* CHAR szLocalFileName[]; (local file name excluding path) */
148     /* packing to dword align start of next field */
149     /* CHAR szHeaderInfo[]; (header info) */
150 } entry_url;
151
152 struct hash_entry
153 {
154     DWORD key;
155     DWORD offset;
156 };
157
158 typedef struct
159 {
160     entry_header header;
161     DWORD next;
162     DWORD id;
163     struct hash_entry hash_table[HASHTABLE_SIZE];
164 } entry_hash_table;
165
166 typedef struct
167 {
168     char signature[28];
169     DWORD size;
170     DWORD hash_table_off;
171     DWORD capacity_in_blocks;
172     DWORD blocks_in_use;
173     DWORD unk1;
174     ULARGE_INTEGER cache_limit;
175     ULARGE_INTEGER cache_usage;
176     ULARGE_INTEGER exempt_usage;
177     DWORD dirs_no;
178     struct _directory_data
179     {
180         DWORD files_no;
181         char name[DIR_LENGTH];
182     } directory_data[MAX_DIR_NO];
183     DWORD options[0x21];
184     BYTE allocation_table[ALLOCATION_TABLE_SIZE];
185 } urlcache_header;
186
187 typedef struct
188 {
189     HANDLE file;
190     CHAR url[1];
191 } stream_handle;
192
193 typedef struct
194 {
195     struct list entry; /* part of a list */
196     char *cache_prefix; /* string that has to be prefixed for this container to be used */
197     LPWSTR path; /* path to url container directory */
198     HANDLE mapping; /* handle of file mapping */
199     DWORD file_size; /* size of file when mapping was opened */
200     HANDLE mutex; /* handle of mutex */
201     DWORD default_entry_type;
202 } cache_container;
203
204 typedef struct
205 {
206     DWORD magic;
207     char *url_search_pattern;
208     DWORD container_idx;
209     DWORD hash_table_idx;
210     DWORD hash_entry_idx;
211 } find_handle;
212
213 /* List of all containers available */
214 static struct list UrlContainers = LIST_INIT(UrlContainers);
215
216 static inline char *heap_strdupWtoUTF8(LPCWSTR str)
217 {
218     char *ret = NULL;
219
220     if(str) {
221         DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
222         ret = heap_alloc(size);
223         if(ret)
224             WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
225     }
226
227     return ret;
228 }
229
230 /***********************************************************************
231  *           urlcache_block_is_free (Internal)
232  *
233  *  Is the specified block number free?
234  *
235  * RETURNS
236  *    zero if free
237  *    non-zero otherwise
238  *
239  */
240 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
241 {
242     BYTE mask = 1 << (block_number%CHAR_BIT);
243     return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
244 }
245
246 /***********************************************************************
247  *           urlcache_block_free (Internal)
248  *
249  *  Marks the specified block as free
250  *
251  * CAUTION
252  *    this function is not updating used blocks count
253  *
254  * RETURNS
255  *    nothing
256  *
257  */
258 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
259 {
260     BYTE mask = ~(1 << (block_number%CHAR_BIT));
261     allocation_table[block_number/CHAR_BIT] &= mask;
262 }
263
264 /***********************************************************************
265  *           urlcache_block_alloc (Internal)
266  *
267  *  Marks the specified block as allocated
268  *
269  * CAUTION
270  *     this function is not updating used blocks count
271  *
272  * RETURNS
273  *    nothing
274  *
275  */
276 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
277 {
278     BYTE mask = 1 << (block_number%CHAR_BIT);
279     allocation_table[block_number/CHAR_BIT] |= mask;
280 }
281
282 /***********************************************************************
283  *           urlcache_entry_alloc (Internal)
284  *
285  *  Finds and allocates the first block of free space big enough and
286  * sets entry to point to it.
287  *
288  * RETURNS
289  *    ERROR_SUCCESS when free memory block was found
290  *    Any other Win32 error code if the entry could not be added
291  *
292  */
293 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
294 {
295     DWORD block, block_size;
296
297     for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
298     {
299         block_size = 0;
300         while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
301                 && urlcache_block_is_free(header->allocation_table, block+block_size))
302             block_size++;
303
304         if(block_size == blocks_needed)
305         {
306             DWORD index;
307
308             TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
309
310             for(index=0; index<blocks_needed; index++)
311                 urlcache_block_alloc(header->allocation_table, block+index);
312
313             *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
314             for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
315                 ((DWORD*)*entry)[index] = 0xdeadbeef;
316             (*entry)->blocks_used = blocks_needed;
317
318             header->blocks_in_use += blocks_needed;
319             return ERROR_SUCCESS;
320         }
321     }
322
323     return ERROR_HANDLE_DISK_FULL;
324 }
325
326 /***********************************************************************
327  *           urlcache_entry_free (Internal)
328  *
329  *  Deletes the specified entry and frees the space allocated to it
330  *
331  * RETURNS
332  *    TRUE if it succeeded
333  *    FALSE if it failed
334  *
335  */
336 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
337 {
338     DWORD start_block, block;
339
340     /* update allocation table */
341     start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
342     for(block = start_block; block < start_block+entry->blocks_used; block++)
343         urlcache_block_free(header->allocation_table, block);
344
345     header->blocks_in_use -= entry->blocks_used;
346     return TRUE;
347 }
348
349 /***********************************************************************
350  *           urlcache_create_hash_table (Internal)
351  *
352  *  Creates a new hash table in free space and adds it to the chain of existing
353  * hash tables.
354  *
355  * RETURNS
356  *    ERROR_SUCCESS if the hash table was created
357  *    ERROR_DISK_FULL if the hash table could not be created
358  *
359  */
360 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
361 {
362     DWORD dwOffset, error;
363     int i;
364
365     if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
366         return error;
367
368     dwOffset = (BYTE*)*hash_table-(BYTE*)header;
369
370     if(hash_table_prev)
371         hash_table_prev->next = dwOffset;
372     else
373         header->hash_table_off = dwOffset;
374
375     (*hash_table)->header.signature = HASH_SIGNATURE;
376     (*hash_table)->next = 0;
377     (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
378     for(i = 0; i < HASHTABLE_SIZE; i++) {
379         (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
380         (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
381     }
382     return ERROR_SUCCESS;
383 }
384
385 /***********************************************************************
386  *           cache_container_create_object_name (Internal)
387  *
388  *  Converts a path to a name suitable for use as a Win32 object name.
389  * Replaces '\\' characters in-place with the specified character
390  * (usually '_' or '!')
391  *
392  * RETURNS
393  *    nothing
394  *
395  */
396 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
397 {
398     for (; *lpszPath; lpszPath++)
399     {
400         if (*lpszPath == '\\')
401             *lpszPath = replace;
402     }
403 }
404
405 /* Caller must hold container lock */
406 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
407 {
408     static const WCHAR mapping_name_format[]
409         = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
410     WCHAR mapping_name[MAX_PATH];
411     HANDLE mapping;
412
413     wsprintfW(mapping_name, mapping_name_format, path, size);
414     cache_container_create_object_name(mapping_name, '_');
415
416     mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
417     if(mapping) {
418         if(validate) *validate = FALSE;
419         return mapping;
420     }
421
422     if(validate) *validate = TRUE;
423     return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
424 }
425
426 /* Caller must hold container lock */
427 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
428 {
429     static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
430         'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
431         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
432         'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
433         'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
434     static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
435
436     DWORD file_size = FILE_SIZE(blocks_no);
437     WCHAR dir_path[MAX_PATH], *dir_name;
438     entry_hash_table *hashtable_entry;
439     urlcache_header *header;
440     HANDLE mapping;
441     FILETIME ft;
442     HKEY key;
443     int i, j;
444
445     if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
446         return GetLastError();
447
448     if(!SetEndOfFile(file))
449         return GetLastError();
450
451     mapping = cache_container_map_index(file, container->path, file_size, NULL);
452     if(!mapping)
453         return GetLastError();
454
455     header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
456     if(!header) {
457         CloseHandle(mapping);
458         return GetLastError();
459     }
460
461     if(blocks_no != MIN_BLOCK_NO) {
462         if(file_size > header->size)
463             memset((char*)header+header->size, 0, file_size-header->size);
464         header->size = file_size;
465         header->capacity_in_blocks = blocks_no;
466
467         UnmapViewOfFile(header);
468         CloseHandle(container->mapping);
469         container->mapping = mapping;
470         container->file_size = file_size;
471         return ERROR_SUCCESS;
472     }
473
474     memset(header, 0, file_size);
475     /* First set some constants and defaults in the header */
476     memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
477     memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
478     header->size = file_size;
479     header->capacity_in_blocks = blocks_no;
480     /* 127MB - taken from default for Windows 2000 */
481     header->cache_limit.QuadPart = 0x07ff5400;
482     /* Copied from a Windows 2000 cache index */
483     header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
484
485     /* If the registry has a cache size set, use the registry value */
486     if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
487         DWORD dw, len = sizeof(dw), keytype;
488
489         if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
490                 keytype == REG_DWORD)
491             header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
492         RegCloseKey(key);
493     }
494
495     urlcache_create_hash_table(header, NULL, &hashtable_entry);
496
497     /* Last step - create the directories */
498     strcpyW(dir_path, container->path);
499     dir_name = dir_path + strlenW(dir_path);
500     dir_name[8] = 0;
501
502     GetSystemTimeAsFileTime(&ft);
503
504     for(i=0; i<header->dirs_no; ++i) {
505         header->directory_data[i].files_no = 0;
506         for(j=0;; ++j) {
507             ULONGLONG n = ft.dwHighDateTime;
508             int k;
509
510             /* Generate a file name to attempt to create.
511              * This algorithm will create what will appear
512              * to be random and unrelated directory names
513              * of up to 9 characters in length.
514              */
515             n <<= 32;
516             n += ft.dwLowDateTime;
517             n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
518
519             for(k = 0; k < 8; ++k) {
520                 int r = (n % 36);
521
522                 /* Dividing by a prime greater than 36 helps
523                  * with the appearance of randomness
524                  */
525                 n /= 37;
526
527                 if(r < 10)
528                     dir_name[k] = '0' + r;
529                 else
530                     dir_name[k] = 'A' + (r - 10);
531             }
532
533             if(CreateDirectoryW(dir_path, 0)) {
534                 /* The following is OK because we generated an
535                  * 8 character directory name made from characters
536                  * [A-Z0-9], which are equivalent for all code
537                  * pages and for UTF-16
538                  */
539                 for (k = 0; k < 8; ++k)
540                     header->directory_data[i].name[k] = dir_name[k];
541                 break;
542             }else if(j >= 255) {
543                 /* Give up. The most likely cause of this
544                  * is a full disk, but whatever the cause
545                  * is, it should be more than apparent that
546                  * we won't succeed.
547                  */
548                 UnmapViewOfFile(header);
549                 CloseHandle(mapping);
550                 return GetLastError();
551             }
552         }
553     }
554
555     UnmapViewOfFile(header);
556     CloseHandle(container->mapping);
557     container->mapping = mapping;
558     container->file_size = file_size;
559     return ERROR_SUCCESS;
560 }
561
562 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
563 {
564     DWORD allocation_size, count_bits, i;
565
566     if(file_size < FILE_SIZE(MIN_BLOCK_NO))
567         return FALSE;
568
569     if(file_size != header->size)
570         return FALSE;
571
572     if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
573             memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
574         return FALSE;
575
576     if(FILE_SIZE(header->capacity_in_blocks) != file_size)
577         return FALSE;
578
579     allocation_size = 0;
580     for(i=0; i<header->capacity_in_blocks/8; i++) {
581         for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
582             if(count_bits & 1)
583                 allocation_size++;
584         }
585     }
586     if(allocation_size != header->blocks_in_use)
587         return FALSE;
588
589     for(; i<ALLOCATION_TABLE_SIZE; i++) {
590         if(header->allocation_table[i])
591             return FALSE;
592     }
593
594     return TRUE;
595 }
596
597 /***********************************************************************
598  *           cache_container_open_index (Internal)
599  *
600  *  Opens the index file and saves mapping handle
601  *
602  * RETURNS
603  *    ERROR_SUCCESS if succeeded
604  *    Any other Win32 error code if failed
605  *
606  */
607 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
608 {
609     static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
610
611     HANDLE file;
612     WCHAR index_path[MAX_PATH];
613     DWORD file_size;
614     BOOL validate;
615
616     WaitForSingleObject(container->mutex, INFINITE);
617
618     if(container->mapping) {
619         ReleaseMutex(container->mutex);
620         return ERROR_SUCCESS;
621     }
622
623     strcpyW(index_path, container->path);
624     strcatW(index_path, index_dat);
625
626     file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
627     if(file == INVALID_HANDLE_VALUE) {
628         /* Maybe the directory wasn't there? Try to create it */
629         if(CreateDirectoryW(container->path, 0))
630             file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
631     }
632     if(file == INVALID_HANDLE_VALUE) {
633         TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
634         ReleaseMutex(container->mutex);
635         return GetLastError();
636     }
637
638     file_size = GetFileSize(file, NULL);
639     if(file_size == INVALID_FILE_SIZE) {
640         CloseHandle(file);
641         ReleaseMutex(container->mutex);
642         return GetLastError();
643     }
644
645     if(blocks_no < MIN_BLOCK_NO)
646         blocks_no = MIN_BLOCK_NO;
647     else if(blocks_no > MAX_BLOCK_NO)
648         blocks_no = MAX_BLOCK_NO;
649
650     if(file_size < FILE_SIZE(blocks_no)) {
651         DWORD ret = cache_container_set_size(container, file, blocks_no);
652         CloseHandle(file);
653         ReleaseMutex(container->mutex);
654         return ret;
655     }
656
657     container->file_size = file_size;
658     container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
659     CloseHandle(file);
660     if(container->mapping && validate) {
661         urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
662
663         if(header && !cache_container_is_valid(header, file_size)) {
664             WARN("detected old or broken index.dat file\n");
665             UnmapViewOfFile(header);
666             FreeUrlCacheSpaceW(container->path, 100, 0);
667         }else if(header) {
668             UnmapViewOfFile(header);
669         }else {
670             CloseHandle(container->mapping);
671             container->mapping = NULL;
672         }
673     }
674
675     if(!container->mapping)
676     {
677         ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
678         ReleaseMutex(container->mutex);
679         return GetLastError();
680     }
681
682     ReleaseMutex(container->mutex);
683     return ERROR_SUCCESS;
684 }
685
686 /***********************************************************************
687  *           cache_container_close_index (Internal)
688  *
689  *  Closes the index
690  *
691  * RETURNS
692  *    nothing
693  *
694  */
695 static void cache_container_close_index(cache_container *pContainer)
696 {
697     CloseHandle(pContainer->mapping);
698     pContainer->mapping = NULL;
699 }
700
701 static BOOL cache_containers_add(LPCWSTR cache_prefix,
702         LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
703 {
704     cache_container *pContainer = heap_alloc(sizeof(cache_container));
705     int cache_prefix_len = strlenW(cache_prefix);
706
707     if (!pContainer)
708     {
709         return FALSE;
710     }
711
712     pContainer->mapping = NULL;
713     pContainer->file_size = 0;
714     pContainer->default_entry_type = default_entry_type;
715
716     pContainer->path = heap_strdupW(path);
717     if (!pContainer->path)
718     {
719         heap_free(pContainer);
720         return FALSE;
721     }
722
723     pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
724     if (!pContainer->cache_prefix)
725     {
726         heap_free(pContainer->path);
727         heap_free(pContainer);
728         return FALSE;
729     }
730
731     memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
732
733     CharLowerW(mutex_name);
734     cache_container_create_object_name(mutex_name, '!');
735
736     if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
737     {
738         ERR("couldn't create mutex (error is %d)\n", GetLastError());
739         heap_free(pContainer->path);
740         heap_free(pContainer);
741         return FALSE;
742     }
743
744     list_add_head(&UrlContainers, &pContainer->entry);
745
746     return TRUE;
747 }
748
749 static void cache_container_delete_container(cache_container *pContainer)
750 {
751     list_remove(&pContainer->entry);
752
753     cache_container_close_index(pContainer);
754     CloseHandle(pContainer->mutex);
755     heap_free(pContainer->path);
756     heap_free(pContainer->cache_prefix);
757     heap_free(pContainer);
758 }
759
760 static void cache_containers_init(void)
761 {
762     static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
763     static const WCHAR UrlPrefix[] = {0};
764     static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
765     static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
766     static const WCHAR CookieSuffix[] = {0};
767     static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
768     static const struct
769     {
770         int nFolder; /* CSIDL_* constant */
771         const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
772         const WCHAR * cache_prefix; /* prefix used to reference the container */
773         DWORD default_entry_type;
774     } DefaultContainerData[] = 
775     {
776         { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
777         { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
778         { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
779     };
780     DWORD i;
781
782     for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
783     {
784         WCHAR wszCachePath[MAX_PATH];
785         WCHAR wszMutexName[MAX_PATH];
786         int path_len, suffix_len;
787         BOOL def_char;
788
789         if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
790         {
791             ERR("Couldn't get path for default container %u\n", i);
792             continue;
793         }
794         path_len = strlenW(wszCachePath);
795         suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
796
797         if (path_len + suffix_len + 2 > MAX_PATH)
798         {
799             ERR("Path too long\n");
800             continue;
801         }
802
803         wszCachePath[path_len] = '\\';
804         wszCachePath[path_len+1] = 0;
805
806         strcpyW(wszMutexName, wszCachePath);
807         
808         if (suffix_len)
809         {
810             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
811             wszCachePath[path_len + suffix_len + 1] = '\\';
812             wszCachePath[path_len + suffix_len + 2] = '\0';
813         }
814
815         if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
816                     NULL, 0, NULL, &def_char) || def_char)
817         {
818             WCHAR tmp[MAX_PATH];
819
820             /* cannot convert path to ANSI code page */
821             if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
822                 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
823                     NULL, 0, NULL, &def_char) || def_char)
824                 ERR("Can't create container path accessible by ANSI functions\n");
825             else
826                 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
827         }
828
829         cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
830                 DefaultContainerData[i].default_entry_type, wszMutexName);
831     }
832 }
833
834 static void cache_containers_free(void)
835 {
836     while(!list_empty(&UrlContainers))
837         cache_container_delete_container(
838             LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
839         );
840 }
841
842 static DWORD cache_containers_find(const char *url, cache_container **ret)
843 {
844     cache_container *container;
845
846     TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
847
848     if(!url)
849         return ERROR_INVALID_PARAMETER;
850
851     LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
852     {
853         int prefix_len = strlen(container->cache_prefix);
854
855         if(!strncmp(container->cache_prefix, url, prefix_len)) {
856             TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
857             *ret = container;
858             return ERROR_SUCCESS;
859         }
860     }
861
862     ERR("no container found\n");
863     return ERROR_FILE_NOT_FOUND;
864 }
865
866 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
867 {
868     DWORD i = 0;
869     cache_container *container;
870
871     TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
872
873     /* non-NULL search pattern only returns one container ever */
874     if (search_pattern && index > 0)
875         return FALSE;
876
877     LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
878     {
879         if (search_pattern)
880         {
881             if (!strcmp(container->cache_prefix, search_pattern))
882             {
883                 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
884                 *ret = container;
885                 return TRUE;
886             }
887         }
888         else
889         {
890             if (i == index)
891             {
892                 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
893                 *ret = container;
894                 return TRUE;
895             }
896         }
897         i++;
898     }
899     return FALSE;
900 }
901
902 /***********************************************************************
903  *           cache_container_lock_index (Internal)
904  *
905  * Locks the index for system-wide exclusive access.
906  *
907  * RETURNS
908  *  Cache file header if successful
909  *  NULL if failed and calls SetLastError.
910  */
911 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
912 {
913     BYTE index;
914     LPVOID pIndexData;
915     urlcache_header* pHeader;
916     DWORD error;
917
918     /* acquire mutex */
919     WaitForSingleObject(pContainer->mutex, INFINITE);
920
921     pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
922
923     if (!pIndexData)
924     {
925         ReleaseMutex(pContainer->mutex);
926         ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
927         return NULL;
928     }
929     pHeader = (urlcache_header*)pIndexData;
930
931     /* file has grown - we need to remap to prevent us getting
932      * access violations when we try and access beyond the end
933      * of the memory mapped file */
934     if (pHeader->size != pContainer->file_size)
935     {
936         UnmapViewOfFile( pHeader );
937         cache_container_close_index(pContainer);
938         error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
939         if (error != ERROR_SUCCESS)
940         {
941             ReleaseMutex(pContainer->mutex);
942             SetLastError(error);
943             return NULL;
944         }
945         pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
946
947         if (!pIndexData)
948         {
949             ReleaseMutex(pContainer->mutex);
950             ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
951             return NULL;
952         }
953         pHeader = (urlcache_header*)pIndexData;
954     }
955
956     TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
957
958     for (index = 0; index < pHeader->dirs_no; index++)
959     {
960         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
961     }
962     
963     return pHeader;
964 }
965
966 /***********************************************************************
967  *           cache_container_unlock_index (Internal)
968  *
969  */
970 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
971 {
972     /* release mutex */
973     ReleaseMutex(pContainer->mutex);
974     return UnmapViewOfFile(pHeader);
975 }
976
977 /***********************************************************************
978  *           urlcache_create_file_pathW (Internal)
979  *
980  *  Copies the full path to the specified buffer given the local file
981  * name and the index of the directory it is in. Always sets value in
982  * lpBufferSize to the required buffer size (in bytes).
983  *
984  * RETURNS
985  *    TRUE if the buffer was big enough
986  *    FALSE if the buffer was too small
987  *
988  */
989 static BOOL urlcache_create_file_pathW(
990     const cache_container *pContainer,
991     const urlcache_header *pHeader,
992     LPCSTR szLocalFileName,
993     BYTE Directory,
994     LPWSTR wszPath,
995     LPLONG lpBufferSize)
996 {
997     LONG nRequired;
998     int path_len = strlenW(pContainer->path);
999     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
1000     if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1001     {
1002         *lpBufferSize = 0;
1003         return FALSE;
1004     }
1005
1006     nRequired = (path_len + file_name_len) * sizeof(WCHAR);
1007     if(Directory != CACHE_CONTAINER_NO_SUBDIR)
1008         nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
1009     if (nRequired <= *lpBufferSize)
1010     {
1011         int dir_len;
1012
1013         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1014         if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1015         {
1016             dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1017             wszPath[dir_len + path_len] = '\\';
1018             dir_len++;
1019         }
1020         else
1021         {
1022             dir_len = 0;
1023         }
1024         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
1025         *lpBufferSize = nRequired;
1026         return TRUE;
1027     }
1028     *lpBufferSize = nRequired;
1029     return FALSE;
1030 }
1031
1032 /***********************************************************************
1033  *           urlcache_create_file_pathA (Internal)
1034  *
1035  *  Copies the full path to the specified buffer given the local file
1036  * name and the index of the directory it is in. Always sets value in
1037  * lpBufferSize to the required buffer size.
1038  *
1039  * RETURNS
1040  *    TRUE if the buffer was big enough
1041  *    FALSE if the buffer was too small
1042  *
1043  */
1044 static BOOL urlcache_create_file_pathA(
1045     const cache_container *pContainer,
1046     const urlcache_header *pHeader,
1047     LPCSTR szLocalFileName,
1048     BYTE Directory,
1049     LPSTR szPath,
1050     LPLONG lpBufferSize)
1051 {
1052     LONG nRequired;
1053     int path_len, file_name_len, dir_len;
1054
1055     if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1056     {
1057         *lpBufferSize = 0;
1058         return FALSE;
1059     }
1060
1061     path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1062     file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1063     if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1064         dir_len = DIR_LENGTH+1;
1065     else
1066         dir_len = 0;
1067
1068     nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1069     if (nRequired <= *lpBufferSize)
1070     {
1071         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1072         if(dir_len) {
1073             memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1074             szPath[path_len + dir_len-1] = '\\';
1075         }
1076         memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1077         *lpBufferSize = nRequired;
1078         return TRUE;
1079     }
1080     *lpBufferSize = nRequired;
1081     return FALSE;
1082 }
1083
1084 /* Just like FileTimeToDosDateTime, except that it also maps the special
1085  * case of a filetime of (0,0) to a DOS date/time of (0,0).
1086  */
1087 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1088                                            WORD *fattime)
1089 {
1090     if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1091         *fatdate = *fattime = 0;
1092     else
1093         FileTimeToDosDateTime(ft, fatdate, fattime);
1094 }
1095
1096 /***********************************************************************
1097  *           urlcache_delete_file (Internal)
1098  */
1099 static DWORD urlcache_delete_file(const cache_container *container,
1100         urlcache_header *header, entry_url *url_entry)
1101 {
1102     WIN32_FILE_ATTRIBUTE_DATA attr;
1103     WCHAR path[MAX_PATH];
1104     LONG path_size = sizeof(path);
1105     DWORD err;
1106     WORD date, time;
1107
1108     if(!url_entry->local_name_off)
1109         goto succ;
1110
1111     if(!urlcache_create_file_pathW(container, header,
1112                 (LPCSTR)url_entry+url_entry->local_name_off,
1113                 url_entry->cache_dir, path, &path_size))
1114         goto succ;
1115
1116     if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1117         goto succ;
1118     file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1119     if(date != url_entry->write_date || time != url_entry->write_time)
1120         goto succ;
1121
1122     err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1123     if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1124         return err;
1125
1126 succ:
1127     if (url_entry->cache_dir < header->dirs_no)
1128     {
1129         if (header->directory_data[url_entry->cache_dir].files_no)
1130             header->directory_data[url_entry->cache_dir].files_no--;
1131     }
1132     if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1133     {
1134         if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1135             header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1136         else
1137             header->exempt_usage.QuadPart = 0;
1138     }
1139     else
1140     {
1141         if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1142             header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1143         else
1144             header->cache_usage.QuadPart = 0;
1145     }
1146
1147     return ERROR_SUCCESS;
1148 }
1149
1150 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1151 {
1152     DWORD *leak_off;
1153     BOOL freed = FALSE;
1154
1155     leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1156     while(*leak_off) {
1157         entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1158
1159         if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1160             *leak_off = url_entry->exempt_delta;
1161             urlcache_entry_free(header, &url_entry->header);
1162             freed = TRUE;
1163         }else {
1164             leak_off = &url_entry->exempt_delta;
1165         }
1166     }
1167
1168     return freed;
1169 }
1170
1171 /***********************************************************************
1172  *           cache_container_clean_index (Internal)
1173  *
1174  * This function is meant to make place in index file by removing leaked
1175  * files entries and resizing the file.
1176  *
1177  * CAUTION: file view may get mapped to new memory
1178  *
1179  * RETURNS
1180  *     ERROR_SUCCESS when new memory is available
1181  *     error code otherwise
1182  */
1183 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1184 {
1185     urlcache_header *header = *file_view;
1186     DWORD ret;
1187
1188     TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1189
1190     if(urlcache_clean_leaked_entries(container, header))
1191         return ERROR_SUCCESS;
1192
1193     if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1194         WARN("index file has maximal size\n");
1195         return ERROR_NOT_ENOUGH_MEMORY;
1196     }
1197
1198     cache_container_close_index(container);
1199     ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1200     if(ret != ERROR_SUCCESS)
1201         return ret;
1202     header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1203     if(!header)
1204         return GetLastError();
1205
1206     UnmapViewOfFile(*file_view);
1207     *file_view = header;
1208     return ERROR_SUCCESS;
1209 }
1210
1211 /* Just like DosDateTimeToFileTime, except that it also maps the special
1212  * case of a DOS date/time of (0,0) to a filetime of (0,0).
1213  */
1214 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1215                                            FILETIME *ft)
1216 {
1217     if (!fatdate && !fattime)
1218         ft->dwLowDateTime = ft->dwHighDateTime = 0;
1219     else
1220         DosDateTimeToFileTime(fatdate, fattime, ft);
1221 }
1222
1223 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1224 {
1225     URL_COMPONENTSA uc;
1226     DWORD len, part_len;
1227     WCHAR *host_name;
1228
1229     memset(&uc, 0, sizeof(uc));
1230     uc.dwStructSize = sizeof(uc);
1231     uc.dwHostNameLength = 1;
1232     if(!InternetCrackUrlA(url, 0, 0, &uc))
1233         uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1234
1235     if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1236         return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1237
1238     if(!decoded_url)
1239         decoded_len = 0;
1240
1241     len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1242     if(!len)
1243         return 0;
1244     if(decoded_url)
1245         decoded_len -= len;
1246
1247     host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1248     if(!host_name)
1249         return 0;
1250     if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1251                 host_name, uc.dwHostNameLength)) {
1252         heap_free(host_name);
1253         return 0;
1254     }
1255     part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1256             decoded_url ? decoded_url+len : NULL, decoded_len);
1257     heap_free(host_name);
1258     if(!part_len) {
1259         SetLastError(ERROR_INTERNET_INVALID_URL);
1260         return 0;
1261     }
1262     len += part_len;
1263     if(decoded_url)
1264         decoded_len -= part_len;
1265
1266     part_len = MultiByteToWideChar(CP_UTF8, 0,
1267             uc.lpszHostName+uc.dwHostNameLength,
1268             -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1269     if(!part_len)
1270         return 0;
1271     len += part_len;
1272
1273     return len;
1274 }
1275
1276 /***********************************************************************
1277  *           urlcache_copy_entry (Internal)
1278  *
1279  *  Copies an entry from the cache index file to the Win32 structure
1280  *
1281  * RETURNS
1282  *    ERROR_SUCCESS if the buffer was big enough
1283  *    ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1284  *
1285  */
1286 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1287         INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1288 {
1289     int url_len;
1290     DWORD size = sizeof(*entry_info);
1291
1292     if(*info_size >= size) {
1293         entry_info->lpHeaderInfo = NULL;
1294         entry_info->lpszFileExtension = NULL;
1295         entry_info->lpszLocalFileName = NULL;
1296         entry_info->lpszSourceUrlName = NULL;
1297         entry_info->CacheEntryType = url_entry->cache_entry_type;
1298         entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1299         entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1300         entry_info->dwHitRate = url_entry->hit_rate;
1301         entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1302         entry_info->dwSizeLow = url_entry->size.u.LowPart;
1303         entry_info->dwStructSize = sizeof(*entry_info);
1304         entry_info->dwUseCount = url_entry->use_count;
1305         dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1306         entry_info->LastAccessTime = url_entry->access_time;
1307         entry_info->LastModifiedTime = url_entry->modification_time;
1308         dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1309     }
1310
1311     if(size%4 && size<*info_size)
1312         ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1313     size = DWORD_ALIGN(size);
1314     if(unicode)
1315         url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1316     else
1317         url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1318     size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1319
1320     if(*info_size >= size) {
1321         DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1322
1323         entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1324         if(unicode)
1325             urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1326         else
1327             memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1328     }
1329
1330     if(size%4 && size<*info_size)
1331         ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1332     size = DWORD_ALIGN(size);
1333
1334     if(url_entry->local_name_off) {
1335         LONG file_name_size;
1336         LPSTR file_name;
1337         file_name = (LPSTR)entry_info+size;
1338         file_name_size = *info_size-size;
1339         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)) ||
1340             (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off, url_entry->cache_dir, file_name, &file_name_size))) {
1341             entry_info->lpszLocalFileName = file_name;
1342         }
1343         size += file_name_size;
1344
1345         if(size%4 && size<*info_size)
1346             ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1347         size = DWORD_ALIGN(size);
1348     }
1349
1350     if(url_entry->header_info_off) {
1351         DWORD header_len;
1352
1353         if(unicode)
1354             header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1355                     url_entry->header_info_size, NULL, 0);
1356         else
1357             header_len = url_entry->header_info_size;
1358         size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1359
1360         if(*info_size >= size) {
1361             DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1362             entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1363             if(unicode)
1364                 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1365                         url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1366             else
1367                 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1368         }
1369         if(size%4 && size<*info_size)
1370             ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1371         size = DWORD_ALIGN(size);
1372     }
1373
1374     if(url_entry->file_extension_off) {
1375         int ext_len;
1376
1377         if(unicode)
1378             ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1379         else
1380             ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1381         size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1382
1383         if(*info_size >= size) {
1384             DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1385             entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1386             if(unicode)
1387                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1388             else
1389                 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1390         }
1391
1392         if(size%4 && size<*info_size)
1393             ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1394         size = DWORD_ALIGN(size);
1395     }
1396
1397     if(size > *info_size) {
1398         *info_size = size;
1399         return ERROR_INSUFFICIENT_BUFFER;
1400     }
1401     *info_size = size;
1402     return ERROR_SUCCESS;
1403 }
1404
1405 /***********************************************************************
1406  *           urlcache_set_entry_info (Internal)
1407  *
1408  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1409  * according to the flags set by field_control.
1410  *
1411  * RETURNS
1412  *    ERROR_SUCCESS if the buffer was big enough
1413  *    ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1414  *
1415  */
1416 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1417 {
1418     if (field_control & CACHE_ENTRY_ACCTIME_FC)
1419         url_entry->access_time = entry_info->LastAccessTime;
1420     if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1421         url_entry->cache_entry_type = entry_info->CacheEntryType;
1422     if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1423         url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1424     if (field_control & CACHE_ENTRY_EXPTIME_FC)
1425         file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1426     if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1427         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1428     if (field_control & CACHE_ENTRY_HITRATE_FC)
1429         url_entry->hit_rate = entry_info->dwHitRate;
1430     if (field_control & CACHE_ENTRY_MODTIME_FC)
1431         url_entry->modification_time = entry_info->LastModifiedTime;
1432     if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1433         file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1434
1435     return ERROR_SUCCESS;
1436 }
1437
1438 /***********************************************************************
1439  *           urlcache_hash_key (Internal)
1440  *
1441  *  Returns the hash key for a given string
1442  *
1443  * RETURNS
1444  *    hash key for the string
1445  *
1446  */
1447 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1448 {
1449     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1450      * but the algorithm and result are not the same!
1451      */
1452     static const unsigned char lookupTable[256] = 
1453     {
1454         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1455         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1456         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1457         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1458         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1459         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1460         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1461         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1462         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1463         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1464         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1465         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1466         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1467         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1468         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1469         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1470         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1471         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1472         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1473         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1474         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1475         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1476         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1477         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1478         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1479         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1480         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1481         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1482         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1483         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1484         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1485         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1486     };
1487     BYTE key[4];
1488     DWORD i;
1489
1490     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1491         key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1492
1493     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1494     {
1495         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1496             key[i] = lookupTable[*lpszKey ^ key[i]];
1497     }
1498
1499     return *(DWORD *)key;
1500 }
1501
1502 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1503 {
1504     if(!dwOffset)
1505         return NULL;
1506     return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1507 }
1508
1509 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1510 {
1511     /* structure of hash table:
1512      *  448 entries divided into 64 blocks
1513      *  each block therefore contains a chain of 7 key/offset pairs
1514      * how position in table is calculated:
1515      *  1. the url is hashed in helper function
1516      *  2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1517      *  3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1518      *
1519      * note:
1520      *  there can be multiple hash tables in the file and the offset to
1521      *  the next one is stored in the header of the hash table
1522      */
1523     DWORD key = urlcache_hash_key(lpszUrl);
1524     DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1525     entry_hash_table* pHashEntry;
1526     DWORD id = 0;
1527
1528     key >>= HASHTABLE_FLAG_BITS;
1529
1530     for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1531          pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1532     {
1533         int i;
1534         if (pHashEntry->id != id++)
1535         {
1536             ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1537             continue;
1538         }
1539         /* make sure that it is in fact a hash entry */
1540         if (pHashEntry->header.signature != HASH_SIGNATURE)
1541         {
1542             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1543             continue;
1544         }
1545
1546         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1547         {
1548             struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1549             if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1550             {
1551                 /* FIXME: we should make sure that this is the right element
1552                  * before returning and claiming that it is. We can do this
1553                  * by doing a simple compare between the URL we were given
1554                  * and the URL stored in the entry. However, this assumes
1555                  * we know the format of all the entries stored in the
1556                  * hash table */
1557                 *ppHashEntry = pHashElement;
1558                 return TRUE;
1559             }
1560         }
1561     }
1562     return FALSE;
1563 }
1564
1565 /***********************************************************************
1566  *           urlcache_hash_entry_set_flags (Internal)
1567  *
1568  *  Sets special bits in hash key
1569  *
1570  * RETURNS
1571  *    nothing
1572  *
1573  */
1574 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1575 {
1576     pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1577 }
1578
1579 /***********************************************************************
1580  *           urlcache_hash_entry_delete (Internal)
1581  *
1582  *  Searches all the hash tables in the index for the given URL and
1583  * then if found deletes the entry.
1584  *
1585  * RETURNS
1586  *    TRUE if the entry was found
1587  *    FALSE if the entry could not be found
1588  *
1589  */
1590 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1591 {
1592     pHashEntry->key = HASHTABLE_DEL;
1593     return TRUE;
1594 }
1595
1596 /***********************************************************************
1597  *           urlcache_hash_entry_create (Internal)
1598  *
1599  *  Searches all the hash tables for a free slot based on the offset
1600  * generated from the hash key. If a free slot is found, the offset and
1601  * key are entered into the hash table.
1602  *
1603  * RETURNS
1604  *    ERROR_SUCCESS if the entry was added
1605  *    Any other Win32 error code if the entry could not be added
1606  *
1607  */
1608 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1609 {
1610     /* see urlcache_find_hash_entry for structure of hash tables */
1611
1612     DWORD key = urlcache_hash_key(lpszUrl);
1613     DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1614     entry_hash_table* pHashEntry, *pHashPrev = NULL;
1615     DWORD id = 0;
1616     DWORD error;
1617
1618     key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1619
1620     for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1621          pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1622     {
1623         int i;
1624         pHashPrev = pHashEntry;
1625
1626         if (pHashEntry->id != id++)
1627         {
1628             ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1629             break;
1630         }
1631         /* make sure that it is in fact a hash entry */
1632         if (pHashEntry->header.signature != HASH_SIGNATURE)
1633         {
1634             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1635             break;
1636         }
1637
1638         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1639         {
1640             struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1641             if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1642             {
1643                 pHashElement->key = key;
1644                 pHashElement->offset = dwOffsetEntry;
1645                 return ERROR_SUCCESS;
1646             }
1647         }
1648     }
1649     error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1650     if (error != ERROR_SUCCESS)
1651         return error;
1652
1653     pHashEntry->hash_table[offset].key = key;
1654     pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1655     return ERROR_SUCCESS;
1656 }
1657
1658 /***********************************************************************
1659  *           urlcache_enum_hash_tables (Internal)
1660  *
1661  *  Enumerates the hash tables in a container.
1662  *
1663  * RETURNS
1664  *    TRUE if an entry was found
1665  *    FALSE if there are no more tables to enumerate.
1666  *
1667  */
1668 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1669 {
1670     for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1671          *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1672     {
1673         TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1674         if ((*ppHashEntry)->id != *id)
1675             continue;
1676         /* make sure that it is in fact a hash entry */
1677         if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1678         {
1679             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1680             (*id)++;
1681             continue;
1682         }
1683
1684         TRACE("hash table number %d found\n", *id);
1685         return TRUE;
1686     }
1687     return FALSE;
1688 }
1689
1690 /***********************************************************************
1691  *           urlcache_enum_hash_table_entries (Internal)
1692  *
1693  *  Enumerates entries in a hash table and returns the next non-free entry.
1694  *
1695  * RETURNS
1696  *    TRUE if an entry was found
1697  *    FALSE if the hash table is empty or there are no more entries to
1698  *     enumerate.
1699  *
1700  */
1701 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1702                                           DWORD * index, const struct hash_entry **ppHashEntry)
1703 {
1704     for (; *index < HASHTABLE_SIZE ; (*index)++)
1705     {
1706         if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1707             continue;
1708
1709         *ppHashEntry = &pHashEntry->hash_table[*index];
1710         TRACE("entry found %d\n", *index);
1711         return TRUE;
1712     }
1713     TRACE("no more entries (%d)\n", *index);
1714     return FALSE;
1715 }
1716
1717 /***********************************************************************
1718  *           cache_container_delete_dir (Internal)
1719  *
1720  *  Erase a directory containing an URL cache.
1721  *
1722  * RETURNS
1723  *    TRUE success, FALSE failure/aborted.
1724  *
1725  */
1726 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1727 {
1728     DWORD path_len;
1729     WCHAR path[MAX_PATH + 1];
1730     SHFILEOPSTRUCTW shfos;
1731     int ret;
1732
1733     path_len = strlenW(lpszPath);
1734     if (path_len >= MAX_PATH)
1735         return FALSE;
1736     strcpyW(path, lpszPath);
1737     path[path_len + 1] = 0;  /* double-NUL-terminate path */
1738
1739     shfos.hwnd = NULL;
1740     shfos.wFunc = FO_DELETE;
1741     shfos.pFrom = path;
1742     shfos.pTo = NULL;
1743     shfos.fFlags = FOF_NOCONFIRMATION;
1744     shfos.fAnyOperationsAborted = FALSE;
1745     ret = SHFileOperationW(&shfos);
1746     if (ret)
1747         ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1748     return !(ret || shfos.fAnyOperationsAborted);
1749 }
1750
1751 /***********************************************************************
1752  *           urlcache_hash_entry_is_locked (Internal)
1753  *
1754  *  Checks if entry is locked. Unlocks it if possible.
1755  */
1756 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1757 {
1758     FILETIME cur_time;
1759     ULARGE_INTEGER acc_time, time;
1760
1761     if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1762         return FALSE;
1763
1764     GetSystemTimeAsFileTime(&cur_time);
1765     time.u.LowPart = cur_time.dwLowDateTime;
1766     time.u.HighPart = cur_time.dwHighDateTime;
1767
1768     acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1769     acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1770
1771     time.QuadPart -= acc_time.QuadPart;
1772
1773     /* check if entry was locked for at least a day */
1774     if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1775         urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1776         url_entry->use_count = 0;
1777         return FALSE;
1778     }
1779
1780     return TRUE;
1781 }
1782
1783 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1784         DWORD *size, DWORD flags, BOOL unicode)
1785 {
1786     urlcache_header *header;
1787     struct hash_entry *hash_entry;
1788     const entry_url *url_entry;
1789     cache_container *container;
1790     DWORD error;
1791
1792     TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1793
1794     if(flags & ~GET_INSTALLED_ENTRY)
1795         FIXME("ignoring unsupported flags: %x\n", flags);
1796
1797     error = cache_containers_find(url, &container);
1798     if(error != ERROR_SUCCESS) {
1799         SetLastError(error);
1800         return FALSE;
1801     }
1802
1803     error = cache_container_open_index(container, MIN_BLOCK_NO);
1804     if(error != ERROR_SUCCESS) {
1805         SetLastError(error);
1806         return FALSE;
1807     }
1808
1809     if(!(header = cache_container_lock_index(container)))
1810         return FALSE;
1811
1812     if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1813         cache_container_unlock_index(container, header);
1814         WARN("entry %s not found!\n", debugstr_a(url));
1815         SetLastError(ERROR_FILE_NOT_FOUND);
1816         return FALSE;
1817     }
1818
1819     url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1820     if(url_entry->header.signature != URL_SIGNATURE) {
1821         cache_container_unlock_index(container, header);
1822         FIXME("Trying to retrieve entry of unknown format %s\n",
1823                 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1824         SetLastError(ERROR_FILE_NOT_FOUND);
1825         return FALSE;
1826     }
1827
1828     TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1829     TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1830                 url_entry->header_info_off, url_entry->header_info_size));
1831
1832     if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1833         cache_container_unlock_index(container, header);
1834         SetLastError(ERROR_FILE_NOT_FOUND);
1835         return FALSE;
1836     }
1837
1838     if(size) {
1839         if(!entry_info)
1840             *size = 0;
1841
1842         error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1843         if(error != ERROR_SUCCESS) {
1844             cache_container_unlock_index(container, header);
1845             SetLastError(error);
1846             return FALSE;
1847         }
1848         if(url_entry->local_name_off)
1849             TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1850     }
1851
1852     cache_container_unlock_index(container, header);
1853     return TRUE;
1854 }
1855
1856 /***********************************************************************
1857  *           GetUrlCacheEntryInfoExA (WININET.@)
1858  *
1859  */
1860 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1861         LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1862         LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1863         LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1864 {
1865     if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1866         ERR("Reserved value was not 0\n");
1867         SetLastError(ERROR_INVALID_PARAMETER);
1868         return FALSE;
1869     }
1870
1871     return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1872             lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1873 }
1874
1875 /***********************************************************************
1876  *           GetUrlCacheEntryInfoA (WININET.@)
1877  *
1878  */
1879 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1880     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1881     LPDWORD lpdwCacheEntryInfoBufferSize)
1882 {
1883     return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1884             lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1885 }
1886
1887 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1888 {
1889     URL_COMPONENTSW uc;
1890     DWORD len, part_len;
1891     WCHAR *punycode;
1892
1893     TRACE("%s\n", debugstr_w(url));
1894
1895     memset(&uc, 0, sizeof(uc));
1896     uc.dwStructSize = sizeof(uc);
1897     uc.dwHostNameLength = 1;
1898     if(!InternetCrackUrlW(url, 0, 0, &uc))
1899         uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1900
1901     if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1902         return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1903
1904     len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1905             encoded_url, encoded_len, NULL, NULL);
1906     if(!len)
1907         return 0;
1908     if(encoded_url)
1909         encoded_len -= len;
1910
1911     part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1912     if(!part_len) {
1913         SetLastError(ERROR_INTERNET_INVALID_URL);
1914         return 0;
1915     }
1916
1917     punycode = heap_alloc(part_len*sizeof(WCHAR));
1918     if(!punycode)
1919         return 0;
1920
1921     part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1922     if(!part_len) {
1923         heap_free(punycode);
1924         return 0;
1925     }
1926
1927     part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1928             encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1929     heap_free(punycode);
1930     if(!part_len)
1931         return 0;
1932     if(encoded_url)
1933         encoded_len -= part_len;
1934     len += part_len;
1935
1936     part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1937             -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1938     if(!part_len)
1939         return 0;
1940     len += part_len;
1941
1942     TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1943     return len;
1944 }
1945
1946 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1947 {
1948     DWORD encoded_len;
1949     char *ret;
1950
1951     encoded_len = urlcache_encode_url(url, NULL, 0);
1952     if(!encoded_len)
1953         return FALSE;
1954
1955     ret = heap_alloc(encoded_len*sizeof(WCHAR));
1956     if(!ret)
1957         return FALSE;
1958
1959     encoded_len = urlcache_encode_url(url, ret, encoded_len);
1960     if(!encoded_len) {
1961         heap_free(ret);
1962         return FALSE;
1963     }
1964
1965     *encoded_url = ret;
1966     return TRUE;
1967 }
1968
1969 /***********************************************************************
1970  *           GetUrlCacheEntryInfoExW (WININET.@)
1971  *
1972  */
1973 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1974         LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1975         LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1976         LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1977 {
1978     char *url;
1979     BOOL ret;
1980
1981     if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1982         ERR("Reserved value was not 0\n");
1983         SetLastError(ERROR_INVALID_PARAMETER);
1984         return FALSE;
1985     }
1986
1987     /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1988     dwFlags &= ~GET_INSTALLED_ENTRY;
1989
1990     if(!urlcache_encode_url_alloc(lpszUrl, &url))
1991         return FALSE;
1992
1993     ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1994             lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1995     heap_free(url);
1996     return ret;
1997 }
1998
1999 /***********************************************************************
2000  *           GetUrlCacheEntryInfoW (WININET.@)
2001  *
2002  */
2003 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2004         LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2005         LPDWORD lpdwCacheEntryInfoBufferSize)
2006 {
2007     return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
2008             lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
2009 }
2010
2011 /***********************************************************************
2012  *           SetUrlCacheEntryInfoA (WININET.@)
2013  */
2014 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
2015         LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2016         DWORD dwFieldControl)
2017 {
2018     urlcache_header *pHeader;
2019     struct hash_entry *pHashEntry;
2020     entry_header *pEntry;
2021     cache_container *pContainer;
2022     DWORD error;
2023
2024     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2025
2026     error = cache_containers_find(lpszUrlName, &pContainer);
2027     if (error != ERROR_SUCCESS)
2028     {
2029         SetLastError(error);
2030         return FALSE;
2031     }
2032
2033     error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2034     if (error != ERROR_SUCCESS)
2035     {
2036         SetLastError(error);
2037         return FALSE;
2038     }
2039
2040     if (!(pHeader = cache_container_lock_index(pContainer)))
2041         return FALSE;
2042
2043     if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2044     {
2045         cache_container_unlock_index(pContainer, pHeader);
2046         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2047         SetLastError(ERROR_FILE_NOT_FOUND);
2048         return FALSE;
2049     }
2050
2051     pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2052     if (pEntry->signature != URL_SIGNATURE)
2053     {
2054         cache_container_unlock_index(pContainer, pHeader);
2055         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2056         SetLastError(ERROR_FILE_NOT_FOUND);
2057         return FALSE;
2058     }
2059
2060     urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2061
2062     cache_container_unlock_index(pContainer, pHeader);
2063
2064     return TRUE;
2065 }
2066
2067 /***********************************************************************
2068  *           SetUrlCacheEntryInfoW (WININET.@)
2069  */
2070 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2071         LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2072         DWORD dwFieldControl)
2073 {
2074     char *url;
2075     BOOL ret;
2076
2077     if(!urlcache_encode_url_alloc(lpszUrl, &url))
2078         return FALSE;
2079
2080     ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2081     heap_free(url);
2082     return ret;
2083 }
2084
2085 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2086 {
2087     urlcache_header *header;
2088     struct hash_entry *hash_entry;
2089     entry_url *url_entry;
2090     cache_container *container;
2091     DWORD error;
2092
2093     TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2094
2095     if(!url || !size || (!entry_info && *size)) {
2096         SetLastError(ERROR_INVALID_PARAMETER);
2097         return FALSE;
2098     }
2099
2100     error = cache_containers_find(url, &container);
2101     if(error != ERROR_SUCCESS) {
2102         SetLastError(error);
2103         return FALSE;
2104     }
2105
2106     error = cache_container_open_index(container, MIN_BLOCK_NO);
2107     if (error != ERROR_SUCCESS) {
2108         SetLastError(error);
2109         return FALSE;
2110     }
2111
2112     if (!(header = cache_container_lock_index(container)))
2113         return FALSE;
2114
2115     if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2116         cache_container_unlock_index(container, header);
2117         TRACE("entry %s not found!\n", url);
2118         SetLastError(ERROR_FILE_NOT_FOUND);
2119         return FALSE;
2120     }
2121
2122     url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2123     if(url_entry->header.signature != URL_SIGNATURE) {
2124         cache_container_unlock_index(container, header);
2125         FIXME("Trying to retrieve entry of unknown format %s\n",
2126                 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2127         SetLastError(ERROR_FILE_NOT_FOUND);
2128         return FALSE;
2129     }
2130
2131     if(!url_entry->local_name_off) {
2132         cache_container_unlock_index(container, header);
2133         SetLastError(ERROR_INVALID_DATA);
2134         return FALSE;
2135     }
2136
2137     TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2138     TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2139                 url_entry->header_info_size));
2140
2141     error = urlcache_copy_entry(container, header, entry_info,
2142             size, url_entry, unicode);
2143     if(error != ERROR_SUCCESS) {
2144         cache_container_unlock_index(container, header);
2145         SetLastError(error);
2146         return FALSE;
2147     }
2148     TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2149
2150     url_entry->hit_rate++;
2151     url_entry->use_count++;
2152     urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2153     GetSystemTimeAsFileTime(&url_entry->access_time);
2154
2155     cache_container_unlock_index(container, header);
2156
2157     return TRUE;
2158 }
2159
2160 /***********************************************************************
2161  *           RetrieveUrlCacheEntryFileA (WININET.@)
2162  *
2163  */
2164 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2165         LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2166         LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2167 {
2168     return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2169             lpdwCacheEntryInfoBufferSize, FALSE);
2170 }
2171
2172 /***********************************************************************
2173  *           RetrieveUrlCacheEntryFileW (WININET.@)
2174  *
2175  */
2176 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2177         LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2178         LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2179 {
2180     char *url;
2181     BOOL ret;
2182
2183     if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2184         return FALSE;
2185
2186     ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2187             lpdwCacheEntryInfoBufferSize, TRUE);
2188     heap_free(url);
2189     return ret;
2190 }
2191
2192 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2193         urlcache_header *pHeader, struct hash_entry *pHashEntry)
2194 {
2195     entry_header *pEntry;
2196     entry_url * pUrlEntry;
2197
2198     pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2199     if (pEntry->signature != URL_SIGNATURE)
2200     {
2201         FIXME("Trying to delete entry of unknown format %s\n",
2202               debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2203         SetLastError(ERROR_FILE_NOT_FOUND);
2204         return FALSE;
2205     }
2206
2207     pUrlEntry = (entry_url *)pEntry;
2208     if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2209     {
2210         TRACE("Trying to delete locked entry\n");
2211         pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2212         SetLastError(ERROR_SHARING_VIOLATION);
2213         return FALSE;
2214     }
2215
2216     if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2217     {
2218         urlcache_entry_free(pHeader, pEntry);
2219     }
2220     else
2221     {
2222         /* Add entry to leaked files list */
2223         pUrlEntry->header.signature = LEAK_SIGNATURE;
2224         pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2225         pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2226     }
2227
2228     urlcache_hash_entry_delete(pHashEntry);
2229     return TRUE;
2230 }
2231
2232 static HANDLE free_cache_running;
2233 static HANDLE dll_unload_event;
2234 static DWORD WINAPI handle_full_cache_worker(void *param)
2235 {
2236     FreeUrlCacheSpaceW(NULL, 20, 0);
2237     ReleaseSemaphore(free_cache_running, 1, NULL);
2238     return 0;
2239 }
2240
2241 static void handle_full_cache(void)
2242 {
2243     if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2244         if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2245             ReleaseSemaphore(free_cache_running, 1, NULL);
2246     }
2247 }
2248
2249 /* Enumerates entries in cache, allows cache unlocking between calls. */
2250 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2251         struct hash_entry **hash_entry, entry_header **entry)
2252 {
2253     entry_hash_table *hashtable_entry;
2254
2255     *hash_entry = NULL;
2256     *entry = NULL;
2257
2258     if(!*hash_table_off) {
2259         *hash_table_off = header->hash_table_off;
2260         *hash_table_entry = 0;
2261
2262         hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2263     }else {
2264         if(*hash_table_off >= header->size) {
2265             *hash_table_off = 0;
2266             return FALSE;
2267         }
2268
2269         hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2270     }
2271
2272     if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2273         *hash_table_off = 0;
2274         return FALSE;
2275     }
2276
2277     while(1) {
2278         if(*hash_table_entry >= HASHTABLE_SIZE) {
2279             *hash_table_off = hashtable_entry->next;
2280             if(!*hash_table_off) {
2281                 *hash_table_off = 0;
2282                 return FALSE;
2283             }
2284
2285             hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2286             *hash_table_entry = 0;
2287         }
2288
2289         if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2290             hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2291             *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2292             *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2293             (*hash_table_entry)++;
2294             return TRUE;
2295         }
2296
2297         (*hash_table_entry)++;
2298     }
2299
2300     *hash_table_off = 0;
2301     return FALSE;
2302 }
2303
2304 /* Rates an urlcache entry to determine if it can be deleted.
2305  *
2306  * Score 0 means that entry can safely be removed, the bigger rating
2307  * the smaller chance of entry being removed.
2308  * DWORD_MAX means that entry can't be deleted at all.
2309  *
2310  * Rating system is currently not fully compatible with native implementation.
2311  */
2312 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2313 {
2314     ULARGE_INTEGER time, access_time;
2315     DWORD rating;
2316
2317     access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2318     access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2319
2320     time.u.LowPart = cur_time->dwLowDateTime;
2321     time.u.HighPart = cur_time->dwHighDateTime;
2322
2323     /* Don't touch entries that were added less than 10 minutes ago */
2324     if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2325         return -1;
2326
2327     if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2328         if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2329             return -1;
2330
2331     time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2332     rating = 400*60*60*24/(60*60*24+time.QuadPart);
2333
2334     if(url_entry->hit_rate > 100)
2335         rating += 100;
2336     else
2337         rating += url_entry->hit_rate;
2338
2339     return rating;
2340 }
2341
2342 static int dword_cmp(const void *p1, const void *p2)
2343 {
2344     return *(const DWORD*)p1 - *(const DWORD*)p2;
2345 }
2346
2347 /***********************************************************************
2348  *           FreeUrlCacheSpaceW (WININET.@)
2349  *
2350  * Frees up some cache.
2351  *
2352  * PARAMETERS
2353  *   cache_path    [I] Which volume to free up from, or NULL if you don't care.
2354  *   size          [I] Percentage of the cache that should be free.
2355  *   filter        [I] Which entries can't be deleted (CacheEntryType)
2356  *
2357  * RETURNS
2358  *   TRUE success. FALSE failure.
2359  *
2360  * IMPLEMENTATION
2361  *   This implementation just retrieves the path of the cache directory, and
2362  *   deletes its contents from the filesystem. The correct approach would
2363  *   probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2364  */
2365 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2366 {
2367     cache_container *container;
2368     DWORD path_len, err;
2369
2370     TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2371
2372     if(size<1 || size>100) {
2373         SetLastError(ERROR_INVALID_PARAMETER);
2374         return FALSE;
2375     }
2376
2377     if(cache_path) {
2378         path_len = strlenW(cache_path);
2379         if(cache_path[path_len-1] == '\\')
2380             path_len--;
2381     }else {
2382         path_len = 0;
2383     }
2384
2385     if(size==100 && !filter) {
2386         LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2387         {
2388             /* When cache_path==NULL only clean Temporary Internet Files */
2389             if((!path_len && container->cache_prefix[0]==0) ||
2390                     (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2391                      (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2392             {
2393                 BOOL ret_del;
2394
2395                 WaitForSingleObject(container->mutex, INFINITE);
2396
2397                 /* unlock, delete, recreate and lock cache */
2398                 cache_container_close_index(container);
2399                 ret_del = cache_container_delete_dir(container->path);
2400                 err = cache_container_open_index(container, MIN_BLOCK_NO);
2401
2402                 ReleaseMutex(container->mutex);
2403                 if(!ret_del || (err != ERROR_SUCCESS))
2404                     return FALSE;
2405             }
2406         }
2407
2408         return TRUE;
2409     }
2410
2411     LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2412     {
2413         urlcache_header *header;
2414         struct hash_entry *hash_entry;
2415         entry_header *entry;
2416         entry_url *url_entry;
2417         ULONGLONG desired_size, cur_size;
2418         DWORD delete_factor, hash_table_off, hash_table_entry;
2419         DWORD rate[100], rate_no;
2420         FILETIME cur_time;
2421
2422         if((path_len || container->cache_prefix[0]!=0) &&
2423                 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2424                  (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2425             continue;
2426
2427         err = cache_container_open_index(container, MIN_BLOCK_NO);
2428         if(err != ERROR_SUCCESS)
2429             continue;
2430
2431         header = cache_container_lock_index(container);
2432         if(!header)
2433             continue;
2434
2435         urlcache_clean_leaked_entries(container, header);
2436
2437         desired_size = header->cache_limit.QuadPart*(100-size)/100;
2438         cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2439         if(cur_size <= desired_size)
2440             delete_factor = 0;
2441         else
2442             delete_factor = (cur_size-desired_size)*100/cur_size;
2443
2444         if(!delete_factor) {
2445             cache_container_unlock_index(container, header);
2446             continue;
2447         }
2448
2449         hash_table_off = 0;
2450         hash_table_entry = 0;
2451         rate_no = 0;
2452         GetSystemTimeAsFileTime(&cur_time);
2453         while(rate_no<sizeof(rate)/sizeof(*rate) &&
2454                 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2455             if(entry->signature != URL_SIGNATURE) {
2456                 WARN("only url entries are currently supported\n");
2457                 continue;
2458             }
2459
2460             url_entry = (entry_url*)entry;
2461             if(url_entry->cache_entry_type & filter)
2462                 continue;
2463
2464             rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2465             if(rate[rate_no] != -1)
2466                 rate_no++;
2467         }
2468
2469         if(!rate_no) {
2470             TRACE("nothing to delete\n");
2471             cache_container_unlock_index(container, header);
2472             continue;
2473         }
2474
2475         qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2476
2477         delete_factor = delete_factor*rate_no/100;
2478         delete_factor = rate[delete_factor];
2479         TRACE("deleting files with rating %d or less\n", delete_factor);
2480
2481         hash_table_off = 0;
2482         while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2483             if(entry->signature != URL_SIGNATURE)
2484                 continue;
2485
2486             url_entry = (entry_url*)entry;
2487             if(url_entry->cache_entry_type & filter)
2488                 continue;
2489
2490             if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2491                 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2492                 urlcache_entry_delete(container, header, hash_entry);
2493
2494                 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2495                     break;
2496
2497                 /* Allow other threads to use cache while cleaning */
2498                 cache_container_unlock_index(container, header);
2499                 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2500                     TRACE("got dll_unload_event - finishing\n");
2501                     return TRUE;
2502                 }
2503                 Sleep(0);
2504                 header = cache_container_lock_index(container);
2505             }
2506         }
2507
2508         TRACE("cache size after cleaning 0x%s/0x%s\n",
2509                 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2510                 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2511         cache_container_unlock_index(container, header);
2512     }
2513
2514     return TRUE;
2515 }
2516
2517 /***********************************************************************
2518  *           FreeUrlCacheSpaceA (WININET.@)
2519  *
2520  * See FreeUrlCacheSpaceW.
2521  */
2522 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2523 {
2524     BOOL ret = FALSE;
2525     LPWSTR path = heap_strdupAtoW(lpszCachePath);
2526     if (lpszCachePath == NULL || path != NULL)
2527         ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2528     heap_free(path);
2529     return ret;
2530 }
2531
2532 /***********************************************************************
2533  *           UnlockUrlCacheEntryFileA (WININET.@)
2534  *
2535  */
2536 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2537 {
2538     urlcache_header *pHeader;
2539     struct hash_entry *pHashEntry;
2540     entry_header *pEntry;
2541     entry_url * pUrlEntry;
2542     cache_container *pContainer;
2543     DWORD error;
2544
2545     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2546
2547     if (dwReserved)
2548     {
2549         ERR("dwReserved != 0\n");
2550         SetLastError(ERROR_INVALID_PARAMETER);
2551         return FALSE;
2552     }
2553
2554     error = cache_containers_find(lpszUrlName, &pContainer);
2555     if (error != ERROR_SUCCESS)
2556     {
2557        SetLastError(error);
2558        return FALSE;
2559     }
2560
2561     error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2562     if (error != ERROR_SUCCESS)
2563     {
2564         SetLastError(error);
2565         return FALSE;
2566     }
2567
2568     if (!(pHeader = cache_container_lock_index(pContainer)))
2569         return FALSE;
2570
2571     if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2572     {
2573         cache_container_unlock_index(pContainer, pHeader);
2574         TRACE("entry %s not found!\n", lpszUrlName);
2575         SetLastError(ERROR_FILE_NOT_FOUND);
2576         return FALSE;
2577     }
2578
2579     pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2580     if (pEntry->signature != URL_SIGNATURE)
2581     {
2582         cache_container_unlock_index(pContainer, pHeader);
2583         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2584         SetLastError(ERROR_FILE_NOT_FOUND);
2585         return FALSE;
2586     }
2587
2588     pUrlEntry = (entry_url *)pEntry;
2589
2590     if (pUrlEntry->use_count == 0)
2591     {
2592         cache_container_unlock_index(pContainer, pHeader);
2593         return FALSE;
2594     }
2595     pUrlEntry->use_count--;
2596     if (!pUrlEntry->use_count)
2597     {
2598         urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2599         if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2600             urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2601     }
2602
2603     cache_container_unlock_index(pContainer, pHeader);
2604
2605     return TRUE;
2606 }
2607
2608 /***********************************************************************
2609  *           UnlockUrlCacheEntryFileW (WININET.@)
2610  *
2611  */
2612 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2613 {
2614     char *url;
2615     BOOL ret;
2616
2617     if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2618         return FALSE;
2619
2620     ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2621     heap_free(url);
2622     return ret;
2623 }
2624
2625 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2626 {
2627     cache_container *container;
2628     urlcache_header *header;
2629     char file_name[MAX_PATH];
2630     WCHAR extW[MAX_PATH];
2631     BYTE cache_dir;
2632     LONG full_path_len;
2633     BOOL generate_name = FALSE;
2634     DWORD error;
2635     HANDLE file;
2636     FILETIME ft;
2637     URL_COMPONENTSA uc;
2638     int i;
2639
2640     TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2641
2642     memset(&uc, 0, sizeof(uc));
2643     uc.dwStructSize = sizeof(uc);
2644     uc.dwUrlPathLength = 1;
2645     uc.dwExtraInfoLength = 1;
2646     if(!InternetCrackUrlA(url, 0, 0, &uc))
2647         uc.dwUrlPathLength = 0;
2648
2649     if(!uc.dwUrlPathLength) {
2650         file_name[0] = 0;
2651     }else {
2652         char *p, *e;
2653
2654         p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2655         while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2656             p--;
2657         if(p>uc.lpszUrlPath && *(p-1)=='.') {
2658             e = p-1;
2659             while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2660                 p--;
2661         }
2662
2663         memcpy(file_name, p, e-p);
2664         file_name[e-p] = 0;
2665
2666         for(p=file_name; *p; p++) {
2667             switch(*p) {
2668             case '<': case '>':
2669             case ':': case '"':
2670             case '|': case '?':
2671             case '*':
2672                 *p = '_'; break;
2673             default: break;
2674             }
2675         }
2676     }
2677
2678     if(!file_name[0])
2679         generate_name = TRUE;
2680
2681     error = cache_containers_find(url, &container);
2682     if(error != ERROR_SUCCESS) {
2683         SetLastError(error);
2684         return FALSE;
2685     }
2686
2687     error = cache_container_open_index(container, MIN_BLOCK_NO);
2688     if(error != ERROR_SUCCESS) {
2689         SetLastError(error);
2690         return FALSE;
2691     }
2692
2693     if(!(header = cache_container_lock_index(container)))
2694         return FALSE;
2695
2696     if(header->dirs_no)
2697         cache_dir = (BYTE)(rand() % header->dirs_no);
2698     else
2699         cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2700
2701     full_path_len = MAX_PATH * sizeof(WCHAR);
2702     if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len)) {
2703         WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2704                 debugstr_a(file_name), full_path_len);
2705         cache_container_unlock_index(container, header);
2706         return FALSE;
2707     }
2708     full_path_len = full_path_len/sizeof(WCHAR) - 1;
2709
2710     cache_container_unlock_index(container, header);
2711
2712     if(ext) {
2713         WCHAR *p;
2714
2715         extW[0] = '.';
2716         MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2717
2718         for(p=extW; *p; p++) {
2719             switch(*p) {
2720             case '<': case '>':
2721             case ':': case '"':
2722             case '|': case '?':
2723             case '*':
2724                 *p = '_'; break;
2725             default: break;
2726             }
2727         }
2728         if(p[-1]==' ' || p[-1]=='.')
2729             p[-1] = '_';
2730     }else {
2731         extW[0] = '\0';
2732     }
2733
2734     for(i=0; i<255 && !generate_name; i++) {
2735         static const WCHAR format[] = {'[','%','u',']','%','s',0};
2736
2737         wsprintfW(full_path+full_path_len, format, i, extW);
2738
2739         TRACE("Trying: %s\n", debugstr_w(full_path));
2740         file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2741         if(file != INVALID_HANDLE_VALUE) {
2742             CloseHandle(file);
2743             return TRUE;
2744         }
2745     }
2746
2747     /* Try to generate random name */
2748     GetSystemTimeAsFileTime(&ft);
2749     strcpyW(full_path+full_path_len+8, extW);
2750
2751     for(i=0; i<255; i++) {
2752         int j;
2753         ULONGLONG n = ft.dwHighDateTime;
2754         n <<= 32;
2755         n += ft.dwLowDateTime;
2756         n ^= (ULONGLONG)i<<48;
2757
2758         for(j=0; j<8; j++) {
2759             int r = (n % 36);
2760             n /= 37;
2761             full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2762         }
2763
2764         TRACE("Trying: %s\n", debugstr_w(full_path));
2765         file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2766         if(file != INVALID_HANDLE_VALUE) {
2767             CloseHandle(file);
2768             return TRUE;
2769         }
2770     }
2771
2772     WARN("Could not find a unique filename\n");
2773     return FALSE;
2774 }
2775
2776 /***********************************************************************
2777  *           CreateUrlCacheEntryA (WININET.@)
2778  *
2779  */
2780 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2781         LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2782 {
2783     WCHAR file_name[MAX_PATH];
2784
2785     if(dwReserved)
2786         FIXME("dwReserved 0x%08x\n", dwReserved);
2787
2788     if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2789         return FALSE;
2790
2791     if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2792         return FALSE;
2793     return TRUE;
2794 }
2795 /***********************************************************************
2796  *           CreateUrlCacheEntryW (WININET.@)
2797  *
2798  */
2799 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2800         LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2801 {
2802     char *url, *ext = NULL;
2803     BOOL ret;
2804
2805     if(dwReserved)
2806         FIXME("dwReserved 0x%08x\n", dwReserved);
2807
2808     if(lpszFileExtension) {
2809         ext = heap_strdupWtoUTF8(lpszFileExtension);
2810         if(!ext)
2811             return FALSE;
2812     }
2813
2814     if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2815         heap_free(ext);
2816         return FALSE;
2817     }
2818
2819     ret = urlcache_entry_create(url, ext, lpszFileName);
2820     heap_free(ext);
2821     heap_free(url);
2822     return ret;
2823 }
2824
2825 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2826     FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2827     BYTE *header_info, DWORD header_size, const char *file_ext,
2828     const char *original_url)
2829 {
2830     cache_container *container;
2831     urlcache_header *header;
2832     struct hash_entry *hash_entry;
2833     entry_header *entry;
2834     entry_url *url_entry;
2835     DWORD url_entry_offset;
2836     DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2837     DWORD file_name_off = 0;
2838     DWORD header_info_off = 0;
2839     DWORD file_ext_off = 0;
2840     WIN32_FILE_ATTRIBUTE_DATA file_attr;
2841     LARGE_INTEGER file_size;
2842     BYTE dir_id;
2843     char file_name_no_container[MAX_PATH];
2844     char *local_file_name = 0;
2845     DWORD hit_rate = 0;
2846     DWORD exempt_delta = 0;
2847     DWORD error;
2848
2849     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2850             entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2851
2852     if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2853         SetLastError(ERROR_INVALID_PARAMETER);
2854         return FALSE;
2855     }
2856     if(original_url)
2857         WARN(": original_url ignored\n");
2858
2859     memset(&file_attr, 0, sizeof(file_attr));
2860     if(file_name) {
2861         if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2862             return FALSE;
2863     }
2864     file_size.u.LowPart = file_attr.nFileSizeLow;
2865     file_size.u.HighPart = file_attr.nFileSizeHigh;
2866
2867     error = cache_containers_find(url, &container);
2868     if(error != ERROR_SUCCESS) {
2869         SetLastError(error);
2870         return FALSE;
2871     }
2872
2873     error = cache_container_open_index(container, MIN_BLOCK_NO);
2874     if(error != ERROR_SUCCESS) {
2875         SetLastError(error);
2876         return FALSE;
2877     }
2878
2879     if(!(header = cache_container_lock_index(container)))
2880         return FALSE;
2881
2882     if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2883         entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2884
2885         if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2886             TRACE("Trying to overwrite locked entry\n");
2887             cache_container_unlock_index(container, header);
2888             SetLastError(ERROR_SHARING_VIOLATION);
2889             return FALSE;
2890         }
2891
2892         hit_rate = url_entry->hit_rate;
2893         exempt_delta = url_entry->exempt_delta;
2894         urlcache_entry_delete(container, header, hash_entry);
2895     }
2896
2897     if(header->dirs_no)
2898         dir_id = 0;
2899     else
2900         dir_id = CACHE_CONTAINER_NO_SUBDIR;
2901
2902     if(file_name) {
2903         BOOL bFound = FALSE;
2904
2905         if(strncmpW(file_name, container->path, lstrlenW(container->path))) {
2906             ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2907             cache_container_unlock_index(container, header);
2908             SetLastError(ERROR_INVALID_PARAMETER);
2909             return FALSE;
2910         }
2911
2912         /* skip container path prefix */
2913         file_name += lstrlenW(container->path);
2914
2915         WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2916         local_file_name = file_name_no_container;
2917
2918         if(header->dirs_no) {
2919             for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2920                 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2921                     bFound = TRUE;
2922                     break;
2923                 }
2924             }
2925
2926             if(!bFound) {
2927                 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2928                 cache_container_unlock_index(container, header);
2929                 SetLastError(ERROR_INVALID_PARAMETER);
2930                 return FALSE;
2931             }
2932
2933             file_name += DIR_LENGTH + 1;
2934             local_file_name += DIR_LENGTH + 1;
2935         }
2936     }
2937
2938     size = DWORD_ALIGN(size + strlen(url) + 1);
2939     if(file_name) {
2940         file_name_off = size;
2941         size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2942     }
2943     if(header_info && header_size) {
2944         header_info_off = size;
2945         size = DWORD_ALIGN(size + header_size);
2946     }
2947     if(file_ext && (file_ext_off = strlen(file_ext))) {
2948         DWORD len = file_ext_off;
2949
2950         file_ext_off = size;
2951         size = DWORD_ALIGN(size + len + 1);
2952     }
2953
2954     /* round up to next block */
2955     if(size % BLOCKSIZE) {
2956         size -= size % BLOCKSIZE;
2957         size += BLOCKSIZE;
2958     }
2959
2960     error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2961     while(error == ERROR_HANDLE_DISK_FULL) {
2962         error = cache_container_clean_index(container, &header);
2963         if(error == ERROR_SUCCESS)
2964             error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2965     }
2966     if(error != ERROR_SUCCESS) {
2967         cache_container_unlock_index(container, header);
2968         SetLastError(error);
2969         return FALSE;
2970     }
2971
2972     /* FindFirstFreeEntry fills in blocks used */
2973     url_entry = (entry_url *)entry;
2974     url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2975     url_entry->header.signature = URL_SIGNATURE;
2976     url_entry->cache_dir = dir_id;
2977     url_entry->cache_entry_type = entry_type | container->default_entry_type;
2978     url_entry->header_info_size = header_size;
2979     if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2980         /* Sticky entries have a default exempt time of one day */
2981         exempt_delta = 86400;
2982     }
2983     url_entry->exempt_delta = exempt_delta;
2984     url_entry->hit_rate = hit_rate+1;
2985     url_entry->file_extension_off = file_ext_off;
2986     url_entry->header_info_off = header_info_off;
2987     url_entry->local_name_off = file_name_off;
2988     url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2989     url_entry->size.QuadPart = file_size.QuadPart;
2990     url_entry->use_count = 0;
2991     GetSystemTimeAsFileTime(&url_entry->access_time);
2992     url_entry->modification_time = modify_time;
2993     file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2994     file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2995     file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2996
2997     /*** Unknowns ***/
2998     url_entry->unk1 = 0;
2999     url_entry->unk2 = 0;
3000     url_entry->unk3 = 0x60;
3001     url_entry->unk4 = 0;
3002     url_entry->unk5 = 0x1010;
3003     url_entry->unk7 = 0;
3004     url_entry->unk8 = 0;
3005
3006
3007     strcpy((LPSTR)url_entry + url_entry->url_off, url);
3008     if(file_name_off)
3009         strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
3010     if(header_info_off)
3011         memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
3012     if(file_ext_off)
3013         strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
3014
3015     error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3016     while(error == ERROR_HANDLE_DISK_FULL) {
3017         error = cache_container_clean_index(container, &header);
3018         if(error == ERROR_SUCCESS) {
3019             url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3020             error = urlcache_hash_entry_create(header, url,
3021                     url_entry_offset, HASHTABLE_URL);
3022         }
3023     }
3024     if(error != ERROR_SUCCESS) {
3025         urlcache_entry_free(header, &url_entry->header);
3026         cache_container_unlock_index(container, header);
3027         SetLastError(error);
3028         return FALSE;
3029     }
3030
3031     if(url_entry->cache_dir < header->dirs_no)
3032         header->directory_data[url_entry->cache_dir].files_no++;
3033     if(entry_type & STICKY_CACHE_ENTRY)
3034         header->exempt_usage.QuadPart += file_size.QuadPart;
3035     else
3036         header->cache_usage.QuadPart += file_size.QuadPart;
3037     if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3038             handle_full_cache();
3039
3040     cache_container_unlock_index(container, header);
3041     return TRUE;
3042 }
3043
3044 /***********************************************************************
3045  *           CommitUrlCacheEntryA (WININET.@)
3046  */
3047 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3048         FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3049         LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3050 {
3051     WCHAR *file_name = NULL;
3052     BOOL ret;
3053
3054     if(lpszLocalFileName) {
3055         file_name = heap_strdupAtoW(lpszLocalFileName);
3056         if(!file_name)
3057             return FALSE;
3058     }
3059
3060     ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3061             CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3062     heap_free(file_name);
3063     return ret;
3064 }
3065
3066 /***********************************************************************
3067  *           CommitUrlCacheEntryW (WININET.@)
3068  */
3069 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3070         FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3071         LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3072 {
3073     char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3074     BOOL ret;
3075
3076     if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3077         return FALSE;
3078
3079     if(lpHeaderInfo) {
3080         header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3081         if(!header_info) {
3082             heap_free(url);
3083             return FALSE;
3084         }
3085         dwHeaderSize = strlen(header_info);
3086     }
3087
3088     if(lpszFileExtension) {
3089         file_ext = heap_strdupWtoA(lpszFileExtension);
3090         if(!file_ext) {
3091             heap_free(url);
3092             heap_free(header_info);
3093             return FALSE;
3094         }
3095     }
3096
3097     if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3098         heap_free(url);
3099         heap_free(header_info);
3100         heap_free(file_ext);
3101     }
3102
3103     ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3104             CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3105     heap_free(url);
3106     heap_free(header_info);
3107     heap_free(file_ext);
3108     heap_free(original_url);
3109     return ret;
3110 }
3111
3112 /***********************************************************************
3113  *           ReadUrlCacheEntryStream (WININET.@)
3114  *
3115  */
3116 BOOL WINAPI ReadUrlCacheEntryStream(
3117     IN HANDLE hUrlCacheStream,
3118     IN  DWORD dwLocation,
3119     IN OUT LPVOID lpBuffer,
3120     IN OUT LPDWORD lpdwLen,
3121     IN DWORD dwReserved
3122     )
3123 {
3124     /* Get handle to file from 'stream' */
3125     stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3126
3127     if (dwReserved != 0)
3128     {
3129         ERR("dwReserved != 0\n");
3130         SetLastError(ERROR_INVALID_PARAMETER);
3131         return FALSE;
3132     }
3133
3134     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3135     {
3136         SetLastError(ERROR_INVALID_HANDLE);
3137         return FALSE;
3138     }
3139
3140     if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3141         return FALSE;
3142     return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3143 }
3144
3145 /***********************************************************************
3146  *           RetrieveUrlCacheEntryStreamA (WININET.@)
3147  *
3148  */
3149 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3150         LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3151         LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3152 {
3153     /* NOTE: this is not the same as the way that the native
3154      * version allocates 'stream' handles. I did it this way
3155      * as it is much easier and no applications should depend
3156      * on this behaviour. (Native version appears to allocate
3157      * indices into a table)
3158      */
3159     stream_handle *stream;
3160     HANDLE file;
3161
3162     TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3163             lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3164
3165     if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3166                 lpdwCacheEntryInfoBufferSize, dwReserved))
3167         return NULL;
3168
3169     file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3170             NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3171     if(file == INVALID_HANDLE_VALUE) {
3172         UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3173         return NULL;
3174     }
3175
3176     /* allocate handle storage space */
3177     stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3178     if(!stream) {
3179         CloseHandle(file);
3180         UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3181         SetLastError(ERROR_OUTOFMEMORY);
3182         return NULL;
3183     }
3184
3185     stream->file = file;
3186     strcpy(stream->url, lpszUrlName);
3187     return stream;
3188 }
3189
3190 /***********************************************************************
3191  *           RetrieveUrlCacheEntryStreamW (WININET.@)
3192  *
3193  */
3194 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3195         LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3196         LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3197 {
3198     DWORD len;
3199     /* NOTE: this is not the same as the way that the native
3200      * version allocates 'stream' handles. I did it this way
3201      * as it is much easier and no applications should depend
3202      * on this behaviour. (Native version appears to allocate
3203      * indices into a table)
3204      */
3205     stream_handle *stream;
3206     HANDLE file;
3207
3208     TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3209             lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3210
3211     if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3212         return NULL;
3213
3214     if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3215                 lpdwCacheEntryInfoBufferSize, dwReserved))
3216         return NULL;
3217
3218     file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3219             NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3220     if(file == INVALID_HANDLE_VALUE) {
3221         UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3222         return NULL;
3223     }
3224
3225     /* allocate handle storage space */
3226     stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3227     if(!stream) {
3228         CloseHandle(file);
3229         UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3230         SetLastError(ERROR_OUTOFMEMORY);
3231         return NULL;
3232     }
3233
3234     stream->file = file;
3235     if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3236         CloseHandle(file);
3237         UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3238         heap_free(stream);
3239         return NULL;
3240     }
3241     return stream;
3242 }
3243
3244 /***********************************************************************
3245  *           UnlockUrlCacheEntryStream (WININET.@)
3246  *
3247  */
3248 BOOL WINAPI UnlockUrlCacheEntryStream(
3249     IN HANDLE hUrlCacheStream,
3250     IN DWORD dwReserved
3251 )
3252 {
3253     stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3254
3255     if (dwReserved != 0)
3256     {
3257         ERR("dwReserved != 0\n");
3258         SetLastError(ERROR_INVALID_PARAMETER);
3259         return FALSE;
3260     }
3261
3262     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3263     {
3264         SetLastError(ERROR_INVALID_HANDLE);
3265         return FALSE;
3266     }
3267
3268     if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3269         return FALSE;
3270
3271     CloseHandle(pStream->file);
3272     heap_free(pStream);
3273     return TRUE;
3274 }
3275
3276
3277 /***********************************************************************
3278  *           DeleteUrlCacheEntryA (WININET.@)
3279  *
3280  */
3281 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3282 {
3283     cache_container *pContainer;
3284     urlcache_header *pHeader;
3285     struct hash_entry *pHashEntry;
3286     DWORD error;
3287     BOOL ret;
3288
3289     TRACE("(%s)\n", debugstr_a(lpszUrlName));
3290
3291     error = cache_containers_find(lpszUrlName, &pContainer);
3292     if (error != ERROR_SUCCESS)
3293     {
3294         SetLastError(error);
3295         return FALSE;
3296     }
3297
3298     error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3299     if (error != ERROR_SUCCESS)
3300     {
3301         SetLastError(error);
3302         return FALSE;
3303     }
3304
3305     if (!(pHeader = cache_container_lock_index(pContainer)))
3306         return FALSE;
3307
3308     if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3309     {
3310         cache_container_unlock_index(pContainer, pHeader);
3311         TRACE("entry %s not found!\n", lpszUrlName);
3312         SetLastError(ERROR_FILE_NOT_FOUND);
3313         return FALSE;
3314     }
3315
3316     ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3317
3318     cache_container_unlock_index(pContainer, pHeader);
3319
3320     return ret;
3321 }
3322
3323 /***********************************************************************
3324  *           DeleteUrlCacheEntryW (WININET.@)
3325  *
3326  */
3327 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3328 {
3329     char *url;
3330     BOOL ret;
3331
3332     if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3333         return FALSE;
3334
3335     ret = DeleteUrlCacheEntryA(url);
3336     heap_free(url);
3337     return ret;
3338 }
3339
3340 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3341 {
3342     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3343     return TRUE;
3344 }
3345
3346 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3347 {
3348     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3349     return TRUE;
3350 }
3351
3352 /***********************************************************************
3353  *           CreateCacheContainerA (WININET.@)
3354  */
3355 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3356                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3357 {
3358     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3359           d1, d2, d3, d4, d5, d6, d7, d8);
3360     return TRUE;
3361 }
3362
3363 /***********************************************************************
3364  *           CreateCacheContainerW (WININET.@)
3365  */
3366 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3367                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3368 {
3369     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3370           d1, d2, d3, d4, d5, d6, d7, d8);
3371     return TRUE;
3372 }
3373
3374 /***********************************************************************
3375  *           FindFirstUrlCacheContainerA (WININET.@)
3376  */
3377 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3378 {
3379     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3380     return NULL;
3381 }
3382
3383 /***********************************************************************
3384  *           FindFirstUrlCacheContainerW (WININET.@)
3385  */
3386 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3387 {
3388     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3389     return NULL;
3390 }
3391
3392 /***********************************************************************
3393  *           FindNextUrlCacheContainerA (WININET.@)
3394  */
3395 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3396 {
3397     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3398     return FALSE;
3399 }
3400
3401 /***********************************************************************
3402  *           FindNextUrlCacheContainerW (WININET.@)
3403  */
3404 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3405 {
3406     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3407     return FALSE;
3408 }
3409
3410 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3411   LPCSTR lpszUrlSearchPattern,
3412   DWORD dwFlags,
3413   DWORD dwFilter,
3414   GROUPID GroupId,
3415   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3416   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3417   LPVOID lpReserved,
3418   LPDWORD pcbReserved2,
3419   LPVOID lpReserved3
3420 )
3421 {
3422     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3423           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3424           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3425     SetLastError(ERROR_FILE_NOT_FOUND);
3426     return NULL;
3427 }
3428
3429 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3430   LPCWSTR lpszUrlSearchPattern,
3431   DWORD dwFlags,
3432   DWORD dwFilter,
3433   GROUPID GroupId,
3434   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3435   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3436   LPVOID lpReserved,
3437   LPDWORD pcbReserved2,
3438   LPVOID lpReserved3
3439 )
3440 {
3441     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3442           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3443           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3444     SetLastError(ERROR_FILE_NOT_FOUND);
3445     return NULL;
3446 }
3447
3448 /***********************************************************************
3449  *           FindFirstUrlCacheEntryA (WININET.@)
3450  *
3451  */
3452 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3453  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3454 {
3455     find_handle *pEntryHandle;
3456
3457     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3458
3459     pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3460     if (!pEntryHandle)
3461         return NULL;
3462
3463     pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3464     if (lpszUrlSearchPattern)
3465     {
3466         pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3467         if (!pEntryHandle->url_search_pattern)
3468         {
3469             heap_free(pEntryHandle);
3470             return NULL;
3471         }
3472     }
3473     else
3474         pEntryHandle->url_search_pattern = NULL;
3475     pEntryHandle->container_idx = 0;
3476     pEntryHandle->hash_table_idx = 0;
3477     pEntryHandle->hash_entry_idx = 0;
3478
3479     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3480     {
3481         heap_free(pEntryHandle);
3482         return NULL;
3483     }
3484     return pEntryHandle;
3485 }
3486
3487 /***********************************************************************
3488  *           FindFirstUrlCacheEntryW (WININET.@)
3489  *
3490  */
3491 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3492  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3493 {
3494     find_handle *pEntryHandle;
3495
3496     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3497
3498     pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3499     if (!pEntryHandle)
3500         return NULL;
3501
3502     pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3503     if (lpszUrlSearchPattern)
3504     {
3505         pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3506         if (!pEntryHandle->url_search_pattern)
3507         {
3508             heap_free(pEntryHandle);
3509             return NULL;
3510         }
3511     }
3512     else
3513         pEntryHandle->url_search_pattern = NULL;
3514     pEntryHandle->container_idx = 0;
3515     pEntryHandle->hash_table_idx = 0;
3516     pEntryHandle->hash_entry_idx = 0;
3517
3518     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3519     {
3520         heap_free(pEntryHandle);
3521         return NULL;
3522     }
3523     return pEntryHandle;
3524 }
3525
3526 static BOOL urlcache_find_next_entry(
3527   HANDLE hEnumHandle,
3528   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3529   LPDWORD lpdwNextCacheEntryInfoBufferSize,
3530   BOOL unicode)
3531 {
3532     find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3533     cache_container *pContainer;
3534
3535     if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3536     {
3537         SetLastError(ERROR_INVALID_HANDLE);
3538         return FALSE;
3539     }
3540
3541     for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3542          pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3543     {
3544         urlcache_header *pHeader;
3545         entry_hash_table *pHashTableEntry;
3546         DWORD error;
3547
3548         error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3549         if (error != ERROR_SUCCESS)
3550         {
3551             SetLastError(error);
3552             return FALSE;
3553         }
3554
3555         if (!(pHeader = cache_container_lock_index(pContainer)))
3556             return FALSE;
3557
3558         for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3559              pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3560         {
3561             const struct hash_entry *pHashEntry = NULL;
3562             for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3563                  pEntryHandle->hash_entry_idx++)
3564             {
3565                 const entry_url *pUrlEntry;
3566                 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3567
3568                 if (pEntry->signature != URL_SIGNATURE)
3569                     continue;
3570
3571                 pUrlEntry = (const entry_url *)pEntry;
3572                 TRACE("Found URL: %s\n",
3573                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3574                 TRACE("Header info: %s\n",
3575                         debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3576                             pUrlEntry->header_info_size));
3577
3578                 error = urlcache_copy_entry(
3579                     pContainer,
3580                     pHeader,
3581                     lpNextCacheEntryInfo,
3582                     lpdwNextCacheEntryInfoBufferSize,
3583                     pUrlEntry,
3584                     unicode);
3585                 if (error != ERROR_SUCCESS)
3586                 {
3587                     cache_container_unlock_index(pContainer, pHeader);
3588                     SetLastError(error);
3589                     return FALSE;
3590                 }
3591                 if(pUrlEntry->local_name_off)
3592                     TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3593
3594                 /* increment the current index so that next time the function
3595                  * is called the next entry is returned */
3596                 pEntryHandle->hash_entry_idx++;
3597                 cache_container_unlock_index(pContainer, pHeader);
3598                 return TRUE;
3599             }
3600         }
3601
3602         cache_container_unlock_index(pContainer, pHeader);
3603     }
3604
3605     SetLastError(ERROR_NO_MORE_ITEMS);
3606     return FALSE;
3607 }
3608
3609 /***********************************************************************
3610  *           FindNextUrlCacheEntryA (WININET.@)
3611  */
3612 BOOL WINAPI FindNextUrlCacheEntryA(
3613   HANDLE hEnumHandle,
3614   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3615   LPDWORD lpdwNextCacheEntryInfoBufferSize)
3616 {
3617     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3618
3619     return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3620             lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3621 }
3622
3623 /***********************************************************************
3624  *           FindNextUrlCacheEntryW (WININET.@)
3625  */
3626 BOOL WINAPI FindNextUrlCacheEntryW(
3627   HANDLE hEnumHandle,
3628   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3629   LPDWORD lpdwNextCacheEntryInfoBufferSize
3630 )
3631 {
3632     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3633
3634     return urlcache_find_next_entry(hEnumHandle,
3635             (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3636             lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3637 }
3638
3639 /***********************************************************************
3640  *           FindCloseUrlCache (WININET.@)
3641  */
3642 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3643 {
3644     find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3645
3646     TRACE("(%p)\n", hEnumHandle);
3647
3648     if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3649     {
3650         SetLastError(ERROR_INVALID_HANDLE);
3651         return FALSE;
3652     }
3653
3654     pEntryHandle->magic = 0;
3655     heap_free(pEntryHandle->url_search_pattern);
3656     heap_free(pEntryHandle);
3657     return TRUE;
3658 }
3659
3660 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3661                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3662 {
3663     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3664           dwSearchCondition, lpGroupId, lpReserved);
3665     return NULL;
3666 }
3667
3668 BOOL WINAPI FindNextUrlCacheEntryExA(
3669   HANDLE hEnumHandle,
3670   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3671   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3672   LPVOID lpReserved,
3673   LPDWORD pcbReserved2,
3674   LPVOID lpReserved3
3675 )
3676 {
3677     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3678           lpReserved, pcbReserved2, lpReserved3);
3679     return FALSE;
3680 }
3681
3682 BOOL WINAPI FindNextUrlCacheEntryExW(
3683   HANDLE hEnumHandle,
3684   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3685   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3686   LPVOID lpReserved,
3687   LPDWORD pcbReserved2,
3688   LPVOID lpReserved3
3689 )
3690 {
3691     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3692           lpReserved, pcbReserved2, lpReserved3);
3693     return FALSE;
3694 }
3695
3696 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3697 {
3698     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3699     return FALSE;
3700 }
3701
3702 /***********************************************************************
3703  *           CreateUrlCacheGroup (WININET.@)
3704  *
3705  */
3706 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3707 {
3708   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3709   return FALSE;
3710 }
3711
3712 /***********************************************************************
3713  *           DeleteUrlCacheGroup (WININET.@)
3714  *
3715  */
3716 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3717 {
3718     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3719           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3720     return FALSE;
3721 }
3722
3723 /***********************************************************************
3724  *           DeleteWpadCacheForNetworks (WININET.@)
3725  *    Undocumented, added in IE8
3726  */
3727 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3728 {
3729     FIXME("(%d) stub\n", unk1);
3730     return FALSE;
3731 }
3732
3733 /***********************************************************************
3734  *           SetUrlCacheEntryGroupA (WININET.@)
3735  *
3736  */
3737 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3738   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3739   LPVOID lpReserved)
3740 {
3741     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3742           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3743           pbGroupAttributes, cbGroupAttributes, lpReserved);
3744     SetLastError(ERROR_FILE_NOT_FOUND);
3745     return FALSE;
3746 }
3747
3748 /***********************************************************************
3749  *           SetUrlCacheEntryGroupW (WININET.@)
3750  *
3751  */
3752 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3753   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3754   LPVOID lpReserved)
3755 {
3756     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3757           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3758           pbGroupAttributes, cbGroupAttributes, lpReserved);
3759     SetLastError(ERROR_FILE_NOT_FOUND);
3760     return FALSE;
3761 }
3762
3763 /***********************************************************************
3764  *           GetUrlCacheConfigInfoW (WININET.@)
3765  */
3766 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3767 {
3768     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3769     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3770     return FALSE;
3771 }
3772
3773 /***********************************************************************
3774  *           GetUrlCacheConfigInfoA (WININET.@)
3775  */
3776 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3777 {
3778     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3779     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3780     return FALSE;
3781 }
3782
3783 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3784                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3785                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3786 {
3787     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3788           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3789           lpdwGroupInfo, lpReserved);
3790     return FALSE;
3791 }
3792
3793 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3794                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3795                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3796 {
3797     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3798           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3799           lpdwGroupInfo, lpReserved);
3800     return FALSE;
3801 }
3802
3803 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3804                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3805 {
3806     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3807           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3808     return TRUE;
3809 }
3810
3811 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3812                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3813 {
3814     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3815           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3816     return TRUE;
3817 }
3818
3819 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3820 {
3821     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3822     return TRUE;
3823 }
3824
3825 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3826 {
3827     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3828     return TRUE;
3829 }
3830
3831 /***********************************************************************
3832  *           DeleteIE3Cache (WININET.@)
3833  *
3834  * Deletes the files used by the IE3 URL caching system.
3835  *
3836  * PARAMS
3837  *   hWnd        [I] A dummy window.
3838  *   hInst       [I] Instance of process calling the function.
3839  *   lpszCmdLine [I] Options used by function.
3840  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
3841  */
3842 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3843 {
3844     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3845     return 0;
3846 }
3847
3848 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3849         FILETIME *pftLastModified)
3850 {
3851     BOOL ret;
3852     FILETIME now, expired;
3853
3854     *pftLastModified = pUrlEntry->modification_time;
3855     GetSystemTimeAsFileTime(&now);
3856     dos_date_time_to_file_time(pUrlEntry->expire_date,
3857             pUrlEntry->expire_time, &expired);
3858     /* If the expired time is 0, it's interpreted as not expired */
3859     if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3860         ret = FALSE;
3861     else
3862         ret = CompareFileTime(&expired, &now) < 0;
3863     return ret;
3864 }
3865
3866 /***********************************************************************
3867  *           IsUrlCacheEntryExpiredA (WININET.@)
3868  *
3869  * PARAMS
3870  *   url             [I] Url
3871  *   dwFlags         [I] Unknown
3872  *   pftLastModified [O] Last modified time
3873  */
3874 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3875 {
3876     urlcache_header *pHeader;
3877     struct hash_entry *pHashEntry;
3878     const entry_header *pEntry;
3879     const entry_url * pUrlEntry;
3880     cache_container *pContainer;
3881     BOOL expired;
3882
3883     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3884
3885     if (!url || !pftLastModified)
3886         return TRUE;
3887     if (dwFlags)
3888         FIXME("unknown flags 0x%08x\n", dwFlags);
3889
3890     /* Any error implies that the URL is expired, i.e. not in the cache */
3891     if (cache_containers_find(url, &pContainer))
3892     {
3893         memset(pftLastModified, 0, sizeof(*pftLastModified));
3894         return TRUE;
3895     }
3896
3897     if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3898     {
3899         memset(pftLastModified, 0, sizeof(*pftLastModified));
3900         return TRUE;
3901     }
3902
3903     if (!(pHeader = cache_container_lock_index(pContainer)))
3904     {
3905         memset(pftLastModified, 0, sizeof(*pftLastModified));
3906         return TRUE;
3907     }
3908
3909     if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3910     {
3911         cache_container_unlock_index(pContainer, pHeader);
3912         memset(pftLastModified, 0, sizeof(*pftLastModified));
3913         TRACE("entry %s not found!\n", url);
3914         return TRUE;
3915     }
3916
3917     pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3918     if (pEntry->signature != URL_SIGNATURE)
3919     {
3920         cache_container_unlock_index(pContainer, pHeader);
3921         memset(pftLastModified, 0, sizeof(*pftLastModified));
3922         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3923         return TRUE;
3924     }
3925
3926     pUrlEntry = (const entry_url *)pEntry;
3927     expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
3928
3929     cache_container_unlock_index(pContainer, pHeader);
3930
3931     return expired;
3932 }
3933
3934 /***********************************************************************
3935  *           IsUrlCacheEntryExpiredW (WININET.@)
3936  *
3937  * PARAMS
3938  *   url             [I] Url
3939  *   dwFlags         [I] Unknown
3940  *   pftLastModified [O] Last modified time
3941  */
3942 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3943 {
3944     char *encoded_url;
3945     BOOL ret;
3946
3947     if(!urlcache_encode_url_alloc(url, &encoded_url))
3948         return FALSE;
3949
3950     ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
3951     heap_free(encoded_url);
3952     return ret;
3953 }
3954
3955 /***********************************************************************
3956  *           GetDiskInfoA (WININET.@)
3957  */
3958 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3959 {
3960     BOOL ret;
3961     ULARGE_INTEGER bytes_free, bytes_total;
3962
3963     TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3964
3965     if (!path)
3966     {
3967         SetLastError(ERROR_INVALID_PARAMETER);
3968         return FALSE;
3969     }
3970
3971     if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3972     {
3973         if (cluster_size) *cluster_size = 1;
3974         if (free) *free = bytes_free.QuadPart;
3975         if (total) *total = bytes_total.QuadPart;
3976     }
3977     return ret;
3978 }
3979
3980 /***********************************************************************
3981  *           RegisterUrlCacheNotification (WININET.@)
3982  */
3983 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3984 {
3985     FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3986     return 0;
3987 }
3988
3989 /***********************************************************************
3990  *           IncrementUrlCacheHeaderData (WININET.@)
3991  */
3992 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3993 {
3994     FIXME("(%u, %p)\n", index, data);
3995     return FALSE;
3996 }
3997
3998 /***********************************************************************
3999  *           RunOnceUrlCache (WININET.@)
4000  */
4001
4002 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4003 {
4004     FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4005     return 0;
4006 }
4007
4008 BOOL init_urlcache(void)
4009 {
4010     dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4011     if(!dll_unload_event)
4012         return FALSE;
4013
4014     free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4015     if(!free_cache_running) {
4016         CloseHandle(dll_unload_event);
4017         return FALSE;
4018     }
4019
4020     cache_containers_init();
4021     return TRUE;
4022 }
4023
4024 void free_urlcache(void)
4025 {
4026     SetEvent(dll_unload_event);
4027     WaitForSingleObject(free_cache_running, INFINITE);
4028     ReleaseSemaphore(free_cache_running, 1, NULL);
4029     CloseHandle(free_cache_running);
4030     CloseHandle(dll_unload_event);
4031
4032     cache_containers_free();
4033 }
4034
4035 /***********************************************************************
4036  *           LoadUrlCacheContent (WININET.@)
4037  */
4038 BOOL WINAPI LoadUrlCacheContent(void)
4039 {
4040     FIXME("stub!\n");
4041     return FALSE;
4042 }