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