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