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