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