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