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