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