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