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