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