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