cmd: Fix a FOR parsing bug when multiple spaces follow an IN.
[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         heap_free(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         heap_free(pContainer->path);
480         heap_free(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         heap_free(pContainer->path);
493         heap_free(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     heap_free(pContainer->path);
509     heap_free(pContainer->cache_prefix);
510     heap_free(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     heap_free(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     heap_free(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             heap_free(file_extension);
2349         }
2350         else
2351         {
2352             dwError = GetLastError();
2353         }
2354         heap_free(url_name);
2355         if (!bSuccess) SetLastError(dwError);
2356     }
2357     return bSuccess;
2358 }
2359 /***********************************************************************
2360  *           CreateUrlCacheEntryW (WININET.@)
2361  *
2362  */
2363 BOOL WINAPI CreateUrlCacheEntryW(
2364     IN LPCWSTR lpszUrlName,
2365     IN DWORD dwExpectedFileSize,
2366     IN LPCWSTR lpszFileExtension,
2367     OUT LPWSTR lpszFileName,
2368     IN DWORD dwReserved
2369 )
2370 {
2371     URLCACHECONTAINER * pContainer;
2372     LPURLCACHE_HEADER pHeader;
2373     CHAR szFile[MAX_PATH];
2374     WCHAR szExtension[MAX_PATH];
2375     LPCWSTR lpszUrlPart;
2376     LPCWSTR lpszUrlEnd;
2377     LPCWSTR lpszFileNameExtension;
2378     LPWSTR lpszFileNameNoPath;
2379     int i;
2380     int countnoextension;
2381     BYTE CacheDir;
2382     LONG lBufferSize;
2383     BOOL bFound = FALSE;
2384     int count;
2385     DWORD error;
2386     HANDLE hFile;
2387     FILETIME ft;
2388
2389     static const WCHAR szWWW[] = {'w','w','w',0};
2390     static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2391
2392     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2393         debugstr_w(lpszUrlName),
2394         dwExpectedFileSize,
2395         debugstr_w(lpszFileExtension),
2396         lpszFileName,
2397         dwReserved);
2398
2399     if (dwReserved)
2400         FIXME("dwReserved 0x%08x\n", dwReserved);
2401
2402    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2403     
2404     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2405         lpszUrlEnd--;
2406
2407     lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2408     if (!lpszUrlPart)
2409         lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2410     if (lpszUrlPart)
2411         lpszUrlEnd = lpszUrlPart;
2412
2413     for (lpszUrlPart = lpszUrlEnd; 
2414         (lpszUrlPart >= lpszUrlName); 
2415         lpszUrlPart--)
2416     {
2417         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2418         {
2419             bFound = TRUE;
2420             lpszUrlPart++;
2421             break;
2422         }
2423     }
2424     if (!lstrcmpW(lpszUrlPart, szWWW))
2425     {
2426         lpszUrlPart += lstrlenW(szWWW);
2427     }
2428
2429     count = lpszUrlEnd - lpszUrlPart;
2430
2431     if (bFound && (count < MAX_PATH))
2432     {
2433         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2434         if (!len)
2435             return FALSE;
2436         szFile[len] = '\0';
2437         while(len && szFile[--len] == '/') szFile[len] = '\0';
2438
2439         /* FIXME: get rid of illegal characters like \, / and : */
2440     }
2441     else
2442     {
2443         FIXME("need to generate a random filename\n");
2444     }
2445
2446     TRACE("File name: %s\n", debugstr_a(szFile));
2447
2448     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2449     if (error != ERROR_SUCCESS)
2450     {
2451         SetLastError(error);
2452         return FALSE;
2453     }
2454
2455     error = URLCacheContainer_OpenIndex(pContainer);
2456     if (error != ERROR_SUCCESS)
2457     {
2458         SetLastError(error);
2459         return FALSE;
2460     }
2461
2462     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2463         return FALSE;
2464
2465     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2466
2467     lBufferSize = MAX_PATH * sizeof(WCHAR);
2468     if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2469     {
2470         WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2471                 debugstr_a(szFile), lBufferSize);
2472         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2473         return FALSE;
2474     }
2475
2476     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2477
2478     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2479         lpszFileNameNoPath >= lpszFileName; 
2480         --lpszFileNameNoPath)
2481     {
2482         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2483             break;
2484     }
2485
2486     countnoextension = lstrlenW(lpszFileNameNoPath);
2487     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2488     if (lpszFileNameExtension)
2489         countnoextension -= lstrlenW(lpszFileNameExtension);
2490     *szExtension = '\0';
2491
2492     if (lpszFileExtension)
2493     {
2494         szExtension[0] = '.';
2495         lstrcpyW(szExtension+1, lpszFileExtension);
2496     }
2497
2498     for (i = 0; i < 255; i++)
2499     {
2500         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2501         WCHAR *p;
2502
2503         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2504         for (p = lpszFileNameNoPath + 1; *p; p++)
2505         {
2506             switch (*p)
2507             {
2508             case '<': case '>':
2509             case ':': case '"':
2510             case '/': case '\\':
2511             case '|': case '?':
2512             case '*':
2513                 *p = '_'; break;
2514             default: break;
2515             }
2516         }
2517         if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2518
2519         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2520         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2521         if (hFile != INVALID_HANDLE_VALUE)
2522         {
2523             CloseHandle(hFile);
2524             return TRUE;
2525         }
2526     }
2527
2528     GetSystemTimeAsFileTime(&ft);
2529     wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2530
2531     TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2532     hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2533     if (hFile != INVALID_HANDLE_VALUE)
2534     {
2535         CloseHandle(hFile);
2536         return TRUE;
2537     }
2538
2539     WARN("Could not find a unique filename\n");
2540     return FALSE;
2541 }
2542
2543
2544 /***********************************************************************
2545  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2546  *
2547  *   The bug we are compensating for is that some drongo at Microsoft
2548  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2549  *   As a consequence, CommitUrlCacheEntryA has been effectively
2550  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2551  *   is still defined as LPCWSTR. The result (other than madness) is
2552  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2553  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2554  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2555  *   result will lose data for arbitrary binary data.
2556  *
2557  */
2558 static BOOL CommitUrlCacheEntryInternal(
2559     IN LPCWSTR lpszUrlName,
2560     IN LPCWSTR lpszLocalFileName,
2561     IN FILETIME ExpireTime,
2562     IN FILETIME LastModifiedTime,
2563     IN DWORD CacheEntryType,
2564     IN LPBYTE lpHeaderInfo,
2565     IN DWORD dwHeaderSize,
2566     IN LPCWSTR lpszFileExtension,
2567     IN LPCWSTR lpszOriginalUrl
2568     )
2569 {
2570     URLCACHECONTAINER * pContainer;
2571     LPURLCACHE_HEADER pHeader;
2572     struct _HASH_ENTRY * pHashEntry;
2573     CACHEFILE_ENTRY * pEntry;
2574     URL_CACHEFILE_ENTRY * pUrlEntry;
2575     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2576     DWORD dwOffsetLocalFileName = 0;
2577     DWORD dwOffsetHeader = 0;
2578     DWORD dwOffsetFileExtension = 0;
2579     LARGE_INTEGER file_size;
2580     BYTE cDirectory = 0;
2581     char achFile[MAX_PATH];
2582     LPSTR lpszUrlNameA = NULL;
2583     LPSTR lpszFileExtensionA = NULL;
2584     char *pchLocalFileName = 0;
2585     DWORD error;
2586
2587     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2588         debugstr_w(lpszUrlName),
2589         debugstr_w(lpszLocalFileName),
2590         CacheEntryType,
2591         lpHeaderInfo,
2592         dwHeaderSize,
2593         debugstr_w(lpszFileExtension),
2594         debugstr_w(lpszOriginalUrl));
2595
2596     if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2597     {
2598         SetLastError(ERROR_INVALID_PARAMETER);
2599         return FALSE;
2600     }
2601     if (lpszOriginalUrl)
2602         WARN(": lpszOriginalUrl ignored\n");
2603
2604     file_size.QuadPart = 0;
2605     if (lpszLocalFileName)
2606     {
2607         HANDLE hFile;
2608
2609         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2610         if (hFile == INVALID_HANDLE_VALUE)
2611         {
2612             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2613             return FALSE;
2614         }
2615
2616         /* Get file size */
2617         if (!GetFileSizeEx(hFile, &file_size))
2618         {
2619             ERR("couldn't get file size (error is %d)\n", GetLastError());
2620             CloseHandle(hFile);
2621             return FALSE;
2622         }
2623
2624         CloseHandle(hFile);
2625     }
2626
2627     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2628     if (error != ERROR_SUCCESS)
2629     {
2630         SetLastError(error);
2631         return FALSE;
2632     }
2633
2634     error = URLCacheContainer_OpenIndex(pContainer);
2635     if (error != ERROR_SUCCESS)
2636     {
2637         SetLastError(error);
2638         return FALSE;
2639     }
2640
2641     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2642         return FALSE;
2643
2644     lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2645     if (!lpszUrlNameA)
2646     {
2647         error = GetLastError();
2648         goto cleanup;
2649     }
2650
2651     if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2652     {
2653         error = GetLastError();
2654         goto cleanup;
2655     }
2656
2657     if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2658     {
2659         FIXME("entry already in cache - don't know what to do!\n");
2660 /*
2661  *        SetLastError(ERROR_FILE_NOT_FOUND);
2662  *        return FALSE;
2663  */
2664         goto cleanup;
2665     }
2666
2667     if (lpszLocalFileName)
2668     {
2669         BOOL bFound = FALSE;
2670
2671         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2672         {
2673             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2674             error = ERROR_INVALID_PARAMETER;
2675             goto cleanup;
2676         }
2677
2678         /* skip container path prefix */
2679         lpszLocalFileName += lstrlenW(pContainer->path);
2680
2681         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2682         pchLocalFileName = achFile;
2683
2684         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2685         {
2686             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2687             {
2688                 bFound = TRUE;
2689                 break;
2690             }
2691         }
2692
2693         if (!bFound)
2694         {
2695             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2696             error = ERROR_INVALID_PARAMETER;
2697             goto cleanup;
2698         }
2699
2700         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2701     }
2702
2703     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2704     if (lpszLocalFileName)
2705     {
2706         dwOffsetLocalFileName = dwBytesNeeded;
2707         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2708     }
2709     if (lpHeaderInfo)
2710     {
2711         dwOffsetHeader = dwBytesNeeded;
2712         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2713     }
2714     if (lpszFileExtensionA)
2715     {
2716         dwOffsetFileExtension = dwBytesNeeded;
2717         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2718     }
2719
2720     /* round up to next block */
2721     if (dwBytesNeeded % BLOCKSIZE)
2722     {
2723         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2724         dwBytesNeeded += BLOCKSIZE;
2725     }
2726
2727     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2728     {
2729         ERR("no free entries\n");
2730         error = ERROR_DISK_FULL;
2731         goto cleanup;
2732     }
2733
2734     /* FindFirstFreeEntry fills in blocks used */
2735     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2736     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2737     pUrlEntry->CacheDir = cDirectory;
2738     pUrlEntry->CacheEntryType = CacheEntryType;
2739     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2740     if (CacheEntryType & STICKY_CACHE_ENTRY)
2741     {
2742         /* Sticky entries have a default exempt time of one day */
2743         pUrlEntry->dwExemptDelta = 86400;
2744     }
2745     else
2746         pUrlEntry->dwExemptDelta = 0;
2747     pUrlEntry->dwHitRate = 0;
2748     pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2749     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2750     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2751     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2752     pUrlEntry->size.QuadPart = file_size.QuadPart;
2753     pUrlEntry->dwUseCount = 0;
2754     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2755     pUrlEntry->LastModifiedTime = LastModifiedTime;
2756     URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2757     URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2758     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2759     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2760
2761     /*** Unknowns ***/
2762     pUrlEntry->dwUnknown1 = 0;
2763     pUrlEntry->dwUnknown2 = 0;
2764     pUrlEntry->dwUnknown3 = 0x60;
2765     pUrlEntry->Unknown4 = 0;
2766     pUrlEntry->wUnknown5 = 0x1010;
2767     pUrlEntry->dwUnknown7 = 0;
2768     pUrlEntry->dwUnknown8 = 0;
2769
2770
2771     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2772     if (dwOffsetLocalFileName)
2773         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2774     if (dwOffsetHeader)
2775         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2776     if (dwOffsetFileExtension)
2777         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2778
2779     error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2780         (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2781     if (error != ERROR_SUCCESS)
2782         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2783     else
2784     {
2785         if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2786             pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2787         if (CacheEntryType & STICKY_CACHE_ENTRY)
2788             pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2789         else
2790             pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2791         if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2792             pHeader->CacheLimit.QuadPart)
2793             FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2794     }
2795
2796 cleanup:
2797     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2798     heap_free(lpszUrlNameA);
2799     heap_free(lpszFileExtensionA);
2800
2801     if (error == ERROR_SUCCESS)
2802         return TRUE;
2803     else
2804     {
2805         SetLastError(error);
2806         return FALSE;
2807     }
2808 }
2809
2810 /***********************************************************************
2811  *           CommitUrlCacheEntryA (WININET.@)
2812  *
2813  */
2814 BOOL WINAPI CommitUrlCacheEntryA(
2815     IN LPCSTR lpszUrlName,
2816     IN LPCSTR lpszLocalFileName,
2817     IN FILETIME ExpireTime,
2818     IN FILETIME LastModifiedTime,
2819     IN DWORD CacheEntryType,
2820     IN LPBYTE lpHeaderInfo,
2821     IN DWORD dwHeaderSize,
2822     IN LPCSTR lpszFileExtension,
2823     IN LPCSTR lpszOriginalUrl
2824     )
2825 {
2826     WCHAR *url_name = NULL;
2827     WCHAR *local_file_name = NULL;
2828     WCHAR *original_url = NULL;
2829     WCHAR *file_extension = NULL;
2830     BOOL bSuccess = FALSE;
2831
2832     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2833         debugstr_a(lpszUrlName),
2834         debugstr_a(lpszLocalFileName),
2835         CacheEntryType,
2836         lpHeaderInfo,
2837         dwHeaderSize,
2838         debugstr_a(lpszFileExtension),
2839         debugstr_a(lpszOriginalUrl));
2840
2841     url_name = heap_strdupAtoW(lpszUrlName);
2842     if (!url_name)
2843         goto cleanup;
2844
2845     if (lpszLocalFileName)
2846     {
2847         local_file_name = heap_strdupAtoW(lpszLocalFileName);
2848         if (!local_file_name)
2849             goto cleanup;
2850     }
2851     if (lpszFileExtension)
2852     {
2853         file_extension = heap_strdupAtoW(lpszFileExtension);
2854         if (!file_extension)
2855             goto cleanup;
2856     }
2857     if (lpszOriginalUrl)
2858     {
2859         original_url = heap_strdupAtoW(lpszOriginalUrl);
2860         if (!original_url)
2861             goto cleanup;
2862     }
2863
2864     bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2865                                            CacheEntryType, lpHeaderInfo, dwHeaderSize,
2866                                            file_extension, original_url);
2867
2868 cleanup:
2869     heap_free(original_url);
2870     heap_free(file_extension);
2871     heap_free(local_file_name);
2872     heap_free(url_name);
2873     return bSuccess;
2874 }
2875
2876 /***********************************************************************
2877  *           CommitUrlCacheEntryW (WININET.@)
2878  *
2879  */
2880 BOOL WINAPI CommitUrlCacheEntryW(
2881     IN LPCWSTR lpszUrlName,
2882     IN LPCWSTR lpszLocalFileName,
2883     IN FILETIME ExpireTime,
2884     IN FILETIME LastModifiedTime,
2885     IN DWORD CacheEntryType,
2886     IN LPWSTR lpHeaderInfo,
2887     IN DWORD dwHeaderSize,
2888     IN LPCWSTR lpszFileExtension,
2889     IN LPCWSTR lpszOriginalUrl
2890     )
2891 {
2892     DWORD dwError = 0;
2893     BOOL bSuccess = FALSE;
2894     DWORD len = 0;
2895     CHAR *header_info = NULL;
2896
2897     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2898         debugstr_w(lpszUrlName),
2899         debugstr_w(lpszLocalFileName),
2900         CacheEntryType,
2901         lpHeaderInfo,
2902         dwHeaderSize,
2903         debugstr_w(lpszFileExtension),
2904         debugstr_w(lpszOriginalUrl));
2905
2906     if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2907     {
2908         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2909                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2910         {
2911                 bSuccess = TRUE;
2912         }
2913         else
2914         {
2915                 dwError = GetLastError();
2916         }
2917         if (header_info)
2918         {
2919             heap_free(header_info);
2920             if (!bSuccess)
2921                 SetLastError(dwError);
2922         }
2923     }
2924     return bSuccess;
2925 }
2926
2927 /***********************************************************************
2928  *           ReadUrlCacheEntryStream (WININET.@)
2929  *
2930  */
2931 BOOL WINAPI ReadUrlCacheEntryStream(
2932     IN HANDLE hUrlCacheStream,
2933     IN  DWORD dwLocation,
2934     IN OUT LPVOID lpBuffer,
2935     IN OUT LPDWORD lpdwLen,
2936     IN DWORD dwReserved
2937     )
2938 {
2939     /* Get handle to file from 'stream' */
2940     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2941
2942     if (dwReserved != 0)
2943     {
2944         ERR("dwReserved != 0\n");
2945         SetLastError(ERROR_INVALID_PARAMETER);
2946         return FALSE;
2947     }
2948
2949     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2950     {
2951         SetLastError(ERROR_INVALID_HANDLE);
2952         return FALSE;
2953     }
2954
2955     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2956         return FALSE;
2957     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2958 }
2959
2960 /***********************************************************************
2961  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2962  *
2963  */
2964 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2965     IN LPCSTR lpszUrlName,
2966     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2967     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2968     IN BOOL fRandomRead,
2969     IN DWORD dwReserved
2970     )
2971 {
2972     /* NOTE: this is not the same as the way that the native
2973      * version allocates 'stream' handles. I did it this way
2974      * as it is much easier and no applications should depend
2975      * on this behaviour. (Native version appears to allocate
2976      * indices into a table)
2977      */
2978     STREAM_HANDLE * pStream;
2979     HANDLE hFile;
2980
2981     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2982            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2983
2984     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2985         lpCacheEntryInfo,
2986         lpdwCacheEntryInfoBufferSize,
2987         dwReserved))
2988     {
2989         return NULL;
2990     }
2991
2992     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2993         GENERIC_READ,
2994         FILE_SHARE_READ,
2995         NULL,
2996         OPEN_EXISTING,
2997         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2998         NULL);
2999     if (hFile == INVALID_HANDLE_VALUE)
3000         return FALSE;
3001     
3002     /* allocate handle storage space */
3003     pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3004     if (!pStream)
3005     {
3006         CloseHandle(hFile);
3007         SetLastError(ERROR_OUTOFMEMORY);
3008         return FALSE;
3009     }
3010
3011     pStream->hFile = hFile;
3012     strcpy(pStream->lpszUrl, lpszUrlName);
3013     return pStream;
3014 }
3015
3016 /***********************************************************************
3017  *           RetrieveUrlCacheEntryStreamW (WININET.@)
3018  *
3019  */
3020 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3021     IN LPCWSTR lpszUrlName,
3022     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3023     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3024     IN BOOL fRandomRead,
3025     IN DWORD dwReserved
3026     )
3027 {
3028     DWORD size;
3029     int url_len;
3030     /* NOTE: this is not the same as the way that the native
3031      * version allocates 'stream' handles. I did it this way
3032      * as it is much easier and no applications should depend
3033      * on this behaviour. (Native version appears to allocate
3034      * indices into a table)
3035      */
3036     STREAM_HANDLE * pStream;
3037     HANDLE hFile;
3038
3039     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3040            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3041
3042     if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3043         lpCacheEntryInfo,
3044         lpdwCacheEntryInfoBufferSize,
3045         dwReserved))
3046     {
3047         return NULL;
3048     }
3049
3050     hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3051         GENERIC_READ,
3052         FILE_SHARE_READ,
3053         NULL,
3054         OPEN_EXISTING,
3055         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3056         NULL);
3057     if (hFile == INVALID_HANDLE_VALUE)
3058         return FALSE;
3059
3060     /* allocate handle storage space */
3061     size = sizeof(STREAM_HANDLE);
3062     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3063     size += url_len;
3064     pStream = heap_alloc(size);
3065     if (!pStream)
3066     {
3067         CloseHandle(hFile);
3068         SetLastError(ERROR_OUTOFMEMORY);
3069         return FALSE;
3070     }
3071
3072     pStream->hFile = hFile;
3073     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3074     return pStream;
3075 }
3076
3077 /***********************************************************************
3078  *           UnlockUrlCacheEntryStream (WININET.@)
3079  *
3080  */
3081 BOOL WINAPI UnlockUrlCacheEntryStream(
3082     IN HANDLE hUrlCacheStream,
3083     IN DWORD dwReserved
3084 )
3085 {
3086     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3087
3088     if (dwReserved != 0)
3089     {
3090         ERR("dwReserved != 0\n");
3091         SetLastError(ERROR_INVALID_PARAMETER);
3092         return FALSE;
3093     }
3094
3095     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3096     {
3097         SetLastError(ERROR_INVALID_HANDLE);
3098         return FALSE;
3099     }
3100
3101     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3102         return FALSE;
3103
3104     CloseHandle(pStream->hFile);
3105     heap_free(pStream);
3106     return TRUE;
3107 }
3108
3109
3110 /***********************************************************************
3111  *           DeleteUrlCacheEntryA (WININET.@)
3112  *
3113  */
3114 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3115 {
3116     URLCACHECONTAINER * pContainer;
3117     LPURLCACHE_HEADER pHeader;
3118     struct _HASH_ENTRY * pHashEntry;
3119     DWORD error;
3120     BOOL ret;
3121
3122     TRACE("(%s)\n", debugstr_a(lpszUrlName));
3123
3124     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3125     if (error != ERROR_SUCCESS)
3126     {
3127         SetLastError(error);
3128         return FALSE;
3129     }
3130
3131     error = URLCacheContainer_OpenIndex(pContainer);
3132     if (error != ERROR_SUCCESS)
3133     {
3134         SetLastError(error);
3135         return FALSE;
3136     }
3137
3138     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3139         return FALSE;
3140
3141     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3142     {
3143         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3144         TRACE("entry %s not found!\n", lpszUrlName);
3145         SetLastError(ERROR_FILE_NOT_FOUND);
3146         return FALSE;
3147     }
3148
3149     ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3150
3151     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3152
3153     return ret;
3154 }
3155
3156 /***********************************************************************
3157  *           DeleteUrlCacheEntryW (WININET.@)
3158  *
3159  */
3160 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3161 {
3162     URLCACHECONTAINER * pContainer;
3163     LPURLCACHE_HEADER pHeader;
3164     struct _HASH_ENTRY * pHashEntry;
3165     LPSTR urlA;
3166     DWORD error;
3167     BOOL ret;
3168
3169     TRACE("(%s)\n", debugstr_w(lpszUrlName));
3170
3171     urlA = heap_strdupWtoA(lpszUrlName);
3172     if (!urlA)
3173     {
3174         SetLastError(ERROR_OUTOFMEMORY);
3175         return FALSE;
3176     }
3177
3178     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3179     if (error != ERROR_SUCCESS)
3180     {
3181         heap_free(urlA);
3182         SetLastError(error);
3183         return FALSE;
3184     }
3185
3186     error = URLCacheContainer_OpenIndex(pContainer);
3187     if (error != ERROR_SUCCESS)
3188     {
3189         heap_free(urlA);
3190         SetLastError(error);
3191         return FALSE;
3192     }
3193
3194     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3195     {
3196         heap_free(urlA);
3197         return FALSE;
3198     }
3199
3200     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3201     {
3202         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3203         TRACE("entry %s not found!\n", debugstr_a(urlA));
3204         heap_free(urlA);
3205         SetLastError(ERROR_FILE_NOT_FOUND);
3206         return FALSE;
3207     }
3208
3209     ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3210
3211     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3212     heap_free(urlA);
3213     return ret;
3214 }
3215
3216 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3217 {
3218     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3219     return TRUE;
3220 }
3221
3222 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3223 {
3224     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3225     return TRUE;
3226 }
3227
3228 /***********************************************************************
3229  *           CreateCacheContainerA (WININET.@)
3230  */
3231 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3232                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3233 {
3234     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3235           d1, d2, d3, d4, d5, d6, d7, d8);
3236     return TRUE;
3237 }
3238
3239 /***********************************************************************
3240  *           CreateCacheContainerW (WININET.@)
3241  */
3242 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3243                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3244 {
3245     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3246           d1, d2, d3, d4, d5, d6, d7, d8);
3247     return TRUE;
3248 }
3249
3250 /***********************************************************************
3251  *           FindFirstUrlCacheContainerA (WININET.@)
3252  */
3253 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3254 {
3255     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3256     return NULL;
3257 }
3258
3259 /***********************************************************************
3260  *           FindFirstUrlCacheContainerW (WININET.@)
3261  */
3262 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3263 {
3264     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3265     return NULL;
3266 }
3267
3268 /***********************************************************************
3269  *           FindNextUrlCacheContainerA (WININET.@)
3270  */
3271 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3272 {
3273     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3274     return FALSE;
3275 }
3276
3277 /***********************************************************************
3278  *           FindNextUrlCacheContainerW (WININET.@)
3279  */
3280 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3281 {
3282     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3283     return FALSE;
3284 }
3285
3286 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3287   LPCSTR lpszUrlSearchPattern,
3288   DWORD dwFlags,
3289   DWORD dwFilter,
3290   GROUPID GroupId,
3291   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3292   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3293   LPVOID lpReserved,
3294   LPDWORD pcbReserved2,
3295   LPVOID lpReserved3
3296 )
3297 {
3298     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3299           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3300           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3301     SetLastError(ERROR_FILE_NOT_FOUND);
3302     return NULL;
3303 }
3304
3305 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3306   LPCWSTR lpszUrlSearchPattern,
3307   DWORD dwFlags,
3308   DWORD dwFilter,
3309   GROUPID GroupId,
3310   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3311   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3312   LPVOID lpReserved,
3313   LPDWORD pcbReserved2,
3314   LPVOID lpReserved3
3315 )
3316 {
3317     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3318           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3319           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3320     SetLastError(ERROR_FILE_NOT_FOUND);
3321     return NULL;
3322 }
3323
3324 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3325
3326 typedef struct URLCacheFindEntryHandle
3327 {
3328     DWORD dwMagic;
3329     LPWSTR lpszUrlSearchPattern;
3330     DWORD dwContainerIndex;
3331     DWORD dwHashTableIndex;
3332     DWORD dwHashEntryIndex;
3333 } URLCacheFindEntryHandle;
3334
3335 /***********************************************************************
3336  *           FindFirstUrlCacheEntryA (WININET.@)
3337  *
3338  */
3339 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3340  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3341 {
3342     URLCacheFindEntryHandle *pEntryHandle;
3343
3344     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3345
3346     pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3347     if (!pEntryHandle)
3348         return NULL;
3349
3350     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3351     if (lpszUrlSearchPattern)
3352     {
3353         pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3354         if (!pEntryHandle->lpszUrlSearchPattern)
3355         {
3356             heap_free(pEntryHandle);
3357             return NULL;
3358         }
3359     }
3360     else
3361         pEntryHandle->lpszUrlSearchPattern = NULL;
3362     pEntryHandle->dwContainerIndex = 0;
3363     pEntryHandle->dwHashTableIndex = 0;
3364     pEntryHandle->dwHashEntryIndex = 0;
3365
3366     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3367     {
3368         heap_free(pEntryHandle);
3369         return NULL;
3370     }
3371     return pEntryHandle;
3372 }
3373
3374 /***********************************************************************
3375  *           FindFirstUrlCacheEntryW (WININET.@)
3376  *
3377  */
3378 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3379  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3380 {
3381     URLCacheFindEntryHandle *pEntryHandle;
3382
3383     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3384
3385     pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3386     if (!pEntryHandle)
3387         return NULL;
3388
3389     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3390     if (lpszUrlSearchPattern)
3391     {
3392         pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3393         if (!pEntryHandle->lpszUrlSearchPattern)
3394         {
3395             heap_free(pEntryHandle);
3396             return NULL;
3397         }
3398     }
3399     else
3400         pEntryHandle->lpszUrlSearchPattern = NULL;
3401     pEntryHandle->dwContainerIndex = 0;
3402     pEntryHandle->dwHashTableIndex = 0;
3403     pEntryHandle->dwHashEntryIndex = 0;
3404
3405     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3406     {
3407         heap_free(pEntryHandle);
3408         return NULL;
3409     }
3410     return pEntryHandle;
3411 }
3412
3413 static BOOL FindNextUrlCacheEntryInternal(
3414   HANDLE hEnumHandle,
3415   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3416   LPDWORD lpdwNextCacheEntryInfoBufferSize,
3417   BOOL unicode)
3418 {
3419     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3420     URLCACHECONTAINER * pContainer;
3421
3422     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3423     {
3424         SetLastError(ERROR_INVALID_HANDLE);
3425         return FALSE;
3426     }
3427
3428     for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3429          pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3430     {
3431         LPURLCACHE_HEADER pHeader;
3432         HASH_CACHEFILE_ENTRY *pHashTableEntry;
3433         DWORD error;
3434
3435         error = URLCacheContainer_OpenIndex(pContainer);
3436         if (error != ERROR_SUCCESS)
3437         {
3438             SetLastError(error);
3439             return FALSE;
3440         }
3441
3442         if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3443             return FALSE;
3444
3445         for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3446              pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3447         {
3448             const struct _HASH_ENTRY *pHashEntry = NULL;
3449             for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3450                  pEntryHandle->dwHashEntryIndex++)
3451             {
3452                 const URL_CACHEFILE_ENTRY *pUrlEntry;
3453                 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3454
3455                 if (pEntry->dwSignature != URL_SIGNATURE)
3456                     continue;
3457
3458                 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3459                 TRACE("Found URL: %s\n",
3460                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3461                 TRACE("Header info: %s\n",
3462                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3463
3464                 error = URLCache_CopyEntry(
3465                     pContainer,
3466                     pHeader,
3467                     lpNextCacheEntryInfo,
3468                     lpdwNextCacheEntryInfoBufferSize,
3469                     pUrlEntry,
3470                     unicode);
3471                 if (error != ERROR_SUCCESS)
3472                 {
3473                     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3474                     SetLastError(error);
3475                     return FALSE;
3476                 }
3477                 TRACE("Local File Name: %s\n",
3478                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3479
3480                 /* increment the current index so that next time the function
3481                  * is called the next entry is returned */
3482                 pEntryHandle->dwHashEntryIndex++;
3483                 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3484                 return TRUE;
3485             }
3486         }
3487
3488         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3489     }
3490
3491     SetLastError(ERROR_NO_MORE_ITEMS);
3492     return FALSE;
3493 }
3494
3495 /***********************************************************************
3496  *           FindNextUrlCacheEntryA (WININET.@)
3497  */
3498 BOOL WINAPI FindNextUrlCacheEntryA(
3499   HANDLE hEnumHandle,
3500   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3501   LPDWORD lpdwNextCacheEntryInfoBufferSize)
3502 {
3503     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3504
3505     return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3506             lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3507 }
3508
3509 /***********************************************************************
3510  *           FindNextUrlCacheEntryW (WININET.@)
3511  */
3512 BOOL WINAPI FindNextUrlCacheEntryW(
3513   HANDLE hEnumHandle,
3514   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3515   LPDWORD lpdwNextCacheEntryInfoBufferSize
3516 )
3517 {
3518     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3519
3520     return FindNextUrlCacheEntryInternal(hEnumHandle,
3521             (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3522             lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3523 }
3524
3525 /***********************************************************************
3526  *           FindCloseUrlCache (WININET.@)
3527  */
3528 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3529 {
3530     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3531
3532     TRACE("(%p)\n", hEnumHandle);
3533
3534     if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3535     {
3536         SetLastError(ERROR_INVALID_HANDLE);
3537         return FALSE;
3538     }
3539
3540     pEntryHandle->dwMagic = 0;
3541     heap_free(pEntryHandle->lpszUrlSearchPattern);
3542     heap_free(pEntryHandle);
3543     return TRUE;
3544 }
3545
3546 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3547                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3548 {
3549     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3550           dwSearchCondition, lpGroupId, lpReserved);
3551     return NULL;
3552 }
3553
3554 BOOL WINAPI FindNextUrlCacheEntryExA(
3555   HANDLE hEnumHandle,
3556   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3557   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3558   LPVOID lpReserved,
3559   LPDWORD pcbReserved2,
3560   LPVOID lpReserved3
3561 )
3562 {
3563     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3564           lpReserved, pcbReserved2, lpReserved3);
3565     return FALSE;
3566 }
3567
3568 BOOL WINAPI FindNextUrlCacheEntryExW(
3569   HANDLE hEnumHandle,
3570   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3571   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3572   LPVOID lpReserved,
3573   LPDWORD pcbReserved2,
3574   LPVOID lpReserved3
3575 )
3576 {
3577     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3578           lpReserved, pcbReserved2, lpReserved3);
3579     return FALSE;
3580 }
3581
3582 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3583 {
3584     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3585     return FALSE;
3586 }
3587
3588 /***********************************************************************
3589  *           CreateUrlCacheGroup (WININET.@)
3590  *
3591  */
3592 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3593 {
3594   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3595   return FALSE;
3596 }
3597
3598 /***********************************************************************
3599  *           DeleteUrlCacheGroup (WININET.@)
3600  *
3601  */
3602 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3603 {
3604     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3605           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3606     return FALSE;
3607 }
3608
3609 /***********************************************************************
3610  *           SetUrlCacheEntryGroupA (WININET.@)
3611  *
3612  */
3613 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3614   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3615   LPVOID lpReserved)
3616 {
3617     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3618           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3619           pbGroupAttributes, cbGroupAttributes, lpReserved);
3620     SetLastError(ERROR_FILE_NOT_FOUND);
3621     return FALSE;
3622 }
3623
3624 /***********************************************************************
3625  *           SetUrlCacheEntryGroupW (WININET.@)
3626  *
3627  */
3628 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3629   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3630   LPVOID lpReserved)
3631 {
3632     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3633           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3634           pbGroupAttributes, cbGroupAttributes, lpReserved);
3635     SetLastError(ERROR_FILE_NOT_FOUND);
3636     return FALSE;
3637 }
3638
3639 /***********************************************************************
3640  *           GetUrlCacheConfigInfoW (WININET.@)
3641  */
3642 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3643 {
3644     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3645     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3646     return FALSE;
3647 }
3648
3649 /***********************************************************************
3650  *           GetUrlCacheConfigInfoA (WININET.@)
3651  */
3652 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3653 {
3654     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3655     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3656     return FALSE;
3657 }
3658
3659 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3660                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3661                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3662 {
3663     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3664           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3665           lpdwGroupInfo, lpReserved);
3666     return FALSE;
3667 }
3668
3669 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3670                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3671                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3672 {
3673     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3674           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3675           lpdwGroupInfo, lpReserved);
3676     return FALSE;
3677 }
3678
3679 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3680                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3681 {
3682     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3683           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3684     return TRUE;
3685 }
3686
3687 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3688                                         LPINTERNET_CACHE_GROUP_INFOW 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 SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3696 {
3697     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3698     return TRUE;
3699 }
3700
3701 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3702 {
3703     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3704     return TRUE;
3705 }
3706
3707 /***********************************************************************
3708  *           DeleteIE3Cache (WININET.@)
3709  *
3710  * Deletes the files used by the IE3 URL caching system.
3711  *
3712  * PARAMS
3713  *   hWnd        [I] A dummy window.
3714  *   hInst       [I] Instance of process calling the function.
3715  *   lpszCmdLine [I] Options used by function.
3716  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
3717  */
3718 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3719 {
3720     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3721     return 0;
3722 }
3723
3724 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3725         FILETIME *pftLastModified)
3726 {
3727     BOOL ret;
3728     FILETIME now, expired;
3729
3730     *pftLastModified = pUrlEntry->LastModifiedTime;
3731     GetSystemTimeAsFileTime(&now);
3732     URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3733             pUrlEntry->wExpiredTime, &expired);
3734     /* If the expired time is 0, it's interpreted as not expired */
3735     if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3736         ret = FALSE;
3737     else
3738         ret = CompareFileTime(&expired, &now) < 0;
3739     return ret;
3740 }
3741
3742 /***********************************************************************
3743  *           IsUrlCacheEntryExpiredA (WININET.@)
3744  *
3745  * PARAMS
3746  *   url             [I] Url
3747  *   dwFlags         [I] Unknown
3748  *   pftLastModified [O] Last modified time
3749  */
3750 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3751 {
3752     LPURLCACHE_HEADER pHeader;
3753     struct _HASH_ENTRY * pHashEntry;
3754     const CACHEFILE_ENTRY * pEntry;
3755     const URL_CACHEFILE_ENTRY * pUrlEntry;
3756     URLCACHECONTAINER * pContainer;
3757     BOOL expired;
3758
3759     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3760
3761     if (!url || !pftLastModified)
3762         return TRUE;
3763     if (dwFlags)
3764         FIXME("unknown flags 0x%08x\n", dwFlags);
3765
3766     /* Any error implies that the URL is expired, i.e. not in the cache */
3767     if (URLCacheContainers_FindContainerA(url, &pContainer))
3768     {
3769         memset(pftLastModified, 0, sizeof(*pftLastModified));
3770         return TRUE;
3771     }
3772
3773     if (URLCacheContainer_OpenIndex(pContainer))
3774     {
3775         memset(pftLastModified, 0, sizeof(*pftLastModified));
3776         return TRUE;
3777     }
3778
3779     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3780     {
3781         memset(pftLastModified, 0, sizeof(*pftLastModified));
3782         return TRUE;
3783     }
3784
3785     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3786     {
3787         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3788         memset(pftLastModified, 0, sizeof(*pftLastModified));
3789         TRACE("entry %s not found!\n", url);
3790         return TRUE;
3791     }
3792
3793     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3794     if (pEntry->dwSignature != URL_SIGNATURE)
3795     {
3796         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3797         memset(pftLastModified, 0, sizeof(*pftLastModified));
3798         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3799         return TRUE;
3800     }
3801
3802     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3803     expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3804
3805     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3806
3807     return expired;
3808 }
3809
3810 /***********************************************************************
3811  *           IsUrlCacheEntryExpiredW (WININET.@)
3812  *
3813  * PARAMS
3814  *   url             [I] Url
3815  *   dwFlags         [I] Unknown
3816  *   pftLastModified [O] Last modified time
3817  */
3818 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3819 {
3820     LPURLCACHE_HEADER pHeader;
3821     struct _HASH_ENTRY * pHashEntry;
3822     const CACHEFILE_ENTRY * pEntry;
3823     const URL_CACHEFILE_ENTRY * pUrlEntry;
3824     URLCACHECONTAINER * pContainer;
3825     BOOL expired;
3826
3827     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3828
3829     if (!url || !pftLastModified)
3830         return TRUE;
3831     if (dwFlags)
3832         FIXME("unknown flags 0x%08x\n", dwFlags);
3833
3834     /* Any error implies that the URL is expired, i.e. not in the cache */
3835     if (URLCacheContainers_FindContainerW(url, &pContainer))
3836     {
3837         memset(pftLastModified, 0, sizeof(*pftLastModified));
3838         return TRUE;
3839     }
3840
3841     if (URLCacheContainer_OpenIndex(pContainer))
3842     {
3843         memset(pftLastModified, 0, sizeof(*pftLastModified));
3844         return TRUE;
3845     }
3846
3847     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3848     {
3849         memset(pftLastModified, 0, sizeof(*pftLastModified));
3850         return TRUE;
3851     }
3852
3853     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3854     {
3855         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3856         memset(pftLastModified, 0, sizeof(*pftLastModified));
3857         TRACE("entry %s not found!\n", debugstr_w(url));
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     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3870     if (pEntry->dwSignature != URL_SIGNATURE)
3871     {
3872         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3873         memset(pftLastModified, 0, sizeof(*pftLastModified));
3874         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3875         return TRUE;
3876     }
3877
3878     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3879     expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3880
3881     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3882
3883     return expired;
3884 }
3885
3886 /***********************************************************************
3887  *           GetDiskInfoA (WININET.@)
3888  */
3889 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3890 {
3891     BOOL ret;
3892     ULARGE_INTEGER bytes_free, bytes_total;
3893
3894     TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3895
3896     if (!path)
3897     {
3898         SetLastError(ERROR_INVALID_PARAMETER);
3899         return FALSE;
3900     }
3901
3902     if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3903     {
3904         if (cluster_size) *cluster_size = 1;
3905         if (free) *free = bytes_free.QuadPart;
3906         if (total) *total = bytes_total.QuadPart;
3907     }
3908     return ret;
3909 }
3910
3911 /***********************************************************************
3912  *           RegisterUrlCacheNotification (WININET.@)
3913  */
3914 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3915 {
3916     FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3917     return 0;
3918 }
3919
3920 /***********************************************************************
3921  *           IncrementUrlCacheHeaderData (WININET.@)
3922  */
3923 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3924 {
3925     FIXME("(%u, %p)\n", index, data);
3926     return FALSE;
3927 }