user32/tests: Fix the listbox delete test on NT4.
[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     {
2226         ERR("dwReserved != 0\n");
2227         SetLastError(ERROR_INVALID_PARAMETER);
2228         return FALSE;
2229     }
2230
2231    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2232     
2233     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2234         lpszUrlEnd--;
2235
2236     for (lpszUrlPart = lpszUrlEnd; 
2237         (lpszUrlPart >= lpszUrlName); 
2238         lpszUrlPart--)
2239     {
2240         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2241         {
2242             bFound = TRUE;
2243             lpszUrlPart++;
2244             break;
2245         }
2246         else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2247         {
2248             lpszUrlEnd = lpszUrlPart;
2249         }
2250     }
2251     if (!lstrcmpW(lpszUrlPart, szWWW))
2252     {
2253         lpszUrlPart += lstrlenW(szWWW);
2254     }
2255
2256     count = lpszUrlEnd - lpszUrlPart;
2257
2258     if (bFound && (count < MAX_PATH))
2259     {
2260         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2261         if (!len)
2262             return FALSE;
2263         szFile[len] = '\0';
2264         while(len && szFile[--len] == '/') szFile[len] = '\0';
2265
2266         /* FIXME: get rid of illegal characters like \, / and : */
2267     }
2268     else
2269     {
2270         FIXME("need to generate a random filename\n");
2271     }
2272
2273     TRACE("File name: %s\n", debugstr_a(szFile));
2274
2275     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2276     if (error != ERROR_SUCCESS)
2277     {
2278         SetLastError(error);
2279         return FALSE;
2280     }
2281
2282     error = URLCacheContainer_OpenIndex(pContainer);
2283     if (error != ERROR_SUCCESS)
2284     {
2285         SetLastError(error);
2286         return FALSE;
2287     }
2288
2289     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2290         return FALSE;
2291
2292     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2293
2294     lBufferSize = MAX_PATH * sizeof(WCHAR);
2295     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2296
2297     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2298
2299     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2300         lpszFileNameNoPath >= lpszFileName; 
2301         --lpszFileNameNoPath)
2302     {
2303         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2304             break;
2305     }
2306
2307     countnoextension = lstrlenW(lpszFileNameNoPath);
2308     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2309     if (lpszFileNameExtension)
2310         countnoextension -= lstrlenW(lpszFileNameExtension);
2311     *szExtension = '\0';
2312
2313     if (lpszFileExtension)
2314     {
2315         szExtension[0] = '.';
2316         lstrcpyW(szExtension+1, lpszFileExtension);
2317     }
2318
2319     for (i = 0; i < 255; i++)
2320     {
2321         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2322         HANDLE hFile;
2323         WCHAR *p;
2324
2325         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2326         for (p = lpszFileNameNoPath + 1; *p; p++)
2327         {
2328             switch (*p)
2329             {
2330             case '<': case '>':
2331             case ':': case '"':
2332             case '/': case '\\':
2333             case '|': case '?':
2334             case '*':
2335                 *p = '_'; break;
2336             default: break;
2337             }
2338         }
2339         if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2340
2341         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2342         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2343         if (hFile != INVALID_HANDLE_VALUE)
2344         {
2345             CloseHandle(hFile);
2346             return TRUE;
2347         }
2348     }
2349
2350     return FALSE;
2351 }
2352
2353
2354 /***********************************************************************
2355  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2356  *
2357  *   The bug we are compensating for is that some drongo at Microsoft
2358  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2359  *   As a consequence, CommitUrlCacheEntryA has been effectively
2360  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2361  *   is still defined as LPCWSTR. The result (other than madness) is
2362  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2363  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2364  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2365  *   result will lose data for arbitrary binary data.
2366  *
2367  */
2368 static BOOL CommitUrlCacheEntryInternal(
2369     IN LPCWSTR lpszUrlName,
2370     IN LPCWSTR lpszLocalFileName,
2371     IN FILETIME ExpireTime,
2372     IN FILETIME LastModifiedTime,
2373     IN DWORD CacheEntryType,
2374     IN LPBYTE lpHeaderInfo,
2375     IN DWORD dwHeaderSize,
2376     IN LPCWSTR lpszFileExtension,
2377     IN LPCWSTR lpszOriginalUrl
2378     )
2379 {
2380     URLCACHECONTAINER * pContainer;
2381     LPURLCACHE_HEADER pHeader;
2382     struct _HASH_ENTRY * pHashEntry;
2383     CACHEFILE_ENTRY * pEntry;
2384     URL_CACHEFILE_ENTRY * pUrlEntry;
2385     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2386     DWORD dwOffsetLocalFileName = 0;
2387     DWORD dwOffsetHeader = 0;
2388     DWORD dwOffsetFileExtension = 0;
2389     DWORD dwFileSizeLow = 0;
2390     DWORD dwFileSizeHigh = 0;
2391     BYTE cDirectory = 0;
2392     int len;
2393     char achFile[MAX_PATH];
2394     LPSTR lpszUrlNameA = NULL;
2395     LPSTR lpszFileExtensionA = NULL;
2396     char *pchLocalFileName = 0;
2397     DWORD error;
2398
2399     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2400         debugstr_w(lpszUrlName),
2401         debugstr_w(lpszLocalFileName),
2402         CacheEntryType,
2403         lpHeaderInfo,
2404         dwHeaderSize,
2405         debugstr_w(lpszFileExtension),
2406         debugstr_w(lpszOriginalUrl));
2407
2408     if (lpszOriginalUrl)
2409         WARN(": lpszOriginalUrl ignored\n");
2410  
2411     if (lpszLocalFileName)
2412     {
2413         HANDLE hFile;
2414
2415         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2416         if (hFile == INVALID_HANDLE_VALUE)
2417         {
2418             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2419             return FALSE;
2420         }
2421
2422         /* Get file size */
2423         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2424         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2425         {
2426             ERR("couldn't get file size (error is %d)\n", GetLastError());
2427             CloseHandle(hFile);
2428             return FALSE;
2429         }
2430
2431         CloseHandle(hFile);
2432     }
2433
2434     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2435     if (error != ERROR_SUCCESS)
2436     {
2437         SetLastError(error);
2438         return FALSE;
2439     }
2440
2441     error = URLCacheContainer_OpenIndex(pContainer);
2442     if (error != ERROR_SUCCESS)
2443     {
2444         SetLastError(error);
2445         return FALSE;
2446     }
2447
2448     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2449         return FALSE;
2450
2451     len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2452     lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2453     if (!lpszUrlNameA)
2454     {
2455         error = GetLastError();
2456         goto cleanup;
2457     }
2458     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2459
2460     if (lpszFileExtension)
2461     {
2462         len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2463         lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2464         if (!lpszFileExtensionA)
2465         {
2466             error = GetLastError();
2467             goto cleanup;
2468         }
2469         WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2470     }
2471
2472     if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2473     {
2474         FIXME("entry already in cache - don't know what to do!\n");
2475 /*
2476  *        SetLastError(ERROR_FILE_NOT_FOUND);
2477  *        return FALSE;
2478  */
2479         goto cleanup;
2480     }
2481
2482     if (lpszLocalFileName)
2483     {
2484         BOOL bFound = FALSE;
2485
2486         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2487         {
2488             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2489             error = ERROR_INVALID_PARAMETER;
2490             goto cleanup;
2491         }
2492
2493         /* skip container path prefix */
2494         lpszLocalFileName += lstrlenW(pContainer->path);
2495
2496         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2497         pchLocalFileName = achFile;
2498
2499         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2500         {
2501             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2502             {
2503                 bFound = TRUE;
2504                 break;
2505             }
2506         }
2507
2508         if (!bFound)
2509         {
2510             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2511             error = ERROR_INVALID_PARAMETER;
2512             goto cleanup;
2513         }
2514
2515         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2516     }
2517
2518     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2519     if (lpszLocalFileName)
2520     {
2521         len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2522         dwOffsetLocalFileName = dwBytesNeeded;
2523         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2524     }
2525     if (lpHeaderInfo)
2526     {
2527         dwOffsetHeader = dwBytesNeeded;
2528         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2529     }
2530     if (lpszFileExtensionA)
2531     {
2532         dwOffsetFileExtension = dwBytesNeeded;
2533         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2534     }
2535
2536     /* round up to next block */
2537     if (dwBytesNeeded % BLOCKSIZE)
2538     {
2539         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2540         dwBytesNeeded += BLOCKSIZE;
2541     }
2542
2543     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2544     {
2545         ERR("no free entries\n");
2546         error = ERROR_DISK_FULL;
2547         goto cleanup;
2548     }
2549
2550     /* FindFirstFreeEntry fills in blocks used */
2551     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2552     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2553     pUrlEntry->CacheDir = cDirectory;
2554     pUrlEntry->CacheEntryType = CacheEntryType;
2555     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2556     pUrlEntry->dwExemptDelta = 0;
2557     pUrlEntry->dwHitRate = 0;
2558     pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2559     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2560     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2561     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2562     pUrlEntry->dwSizeHigh = 0;
2563     pUrlEntry->dwSizeLow = dwFileSizeLow;
2564     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2565     pUrlEntry->dwUseCount = 0;
2566     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2567     pUrlEntry->LastModifiedTime = LastModifiedTime;
2568     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2569     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2570     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2571     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2572
2573     /*** Unknowns ***/
2574     pUrlEntry->dwUnknown1 = 0;
2575     pUrlEntry->dwUnknown2 = 0;
2576     pUrlEntry->dwUnknown3 = 0x60;
2577     pUrlEntry->Unknown4 = 0;
2578     pUrlEntry->wUnknown5 = 0x1010;
2579     pUrlEntry->dwUnknown7 = 0;
2580     pUrlEntry->dwUnknown8 = 0;
2581
2582
2583     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2584     if (dwOffsetLocalFileName)
2585         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2586     if (dwOffsetHeader)
2587         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2588     if (dwOffsetFileExtension)
2589         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2590
2591     error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2592         (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2593     if (error != ERROR_SUCCESS)
2594         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2595
2596 cleanup:
2597     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2598     HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2599     HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2600
2601     if (error == ERROR_SUCCESS)
2602         return TRUE;
2603     else
2604     {
2605         SetLastError(error);
2606         return FALSE;
2607     }
2608 }
2609
2610 /***********************************************************************
2611  *           CommitUrlCacheEntryA (WININET.@)
2612  *
2613  */
2614 BOOL WINAPI CommitUrlCacheEntryA(
2615     IN LPCSTR lpszUrlName,
2616     IN LPCSTR lpszLocalFileName,
2617     IN FILETIME ExpireTime,
2618     IN FILETIME LastModifiedTime,
2619     IN DWORD CacheEntryType,
2620     IN LPBYTE lpHeaderInfo,
2621     IN DWORD dwHeaderSize,
2622     IN LPCSTR lpszFileExtension,
2623     IN LPCSTR lpszOriginalUrl
2624     )
2625 {
2626     DWORD len;
2627     WCHAR *url_name = NULL;
2628     WCHAR *local_file_name = NULL;
2629     WCHAR *original_url = NULL;
2630     WCHAR *file_extension = NULL;
2631     BOOL bSuccess = FALSE;
2632
2633     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2634         debugstr_a(lpszUrlName),
2635         debugstr_a(lpszLocalFileName),
2636         CacheEntryType,
2637         lpHeaderInfo,
2638         dwHeaderSize,
2639         debugstr_a(lpszFileExtension),
2640         debugstr_a(lpszOriginalUrl));
2641
2642     len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2643     url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2644     if (!url_name)
2645         goto cleanup;
2646     MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2647
2648     if (lpszLocalFileName)
2649     {
2650         len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2651         local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2652         if (!local_file_name)
2653             goto cleanup;
2654         MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2655     }
2656     if (lpszFileExtension)
2657     {
2658         len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2659         file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2660         if (!file_extension)
2661             goto cleanup;
2662         MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2663     }
2664     if (lpszOriginalUrl)
2665     {
2666         len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2667         original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2668         if (!original_url)
2669             goto cleanup;
2670         MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2671     }
2672
2673     bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2674                                            CacheEntryType, lpHeaderInfo, dwHeaderSize,
2675                                            file_extension, original_url);
2676
2677 cleanup:
2678     HeapFree(GetProcessHeap(), 0, original_url);
2679     HeapFree(GetProcessHeap(), 0, file_extension);
2680     HeapFree(GetProcessHeap(), 0, local_file_name);
2681     HeapFree(GetProcessHeap(), 0, url_name);
2682
2683     return bSuccess;
2684 }
2685
2686 /***********************************************************************
2687  *           CommitUrlCacheEntryW (WININET.@)
2688  *
2689  */
2690 BOOL WINAPI CommitUrlCacheEntryW(
2691     IN LPCWSTR lpszUrlName,
2692     IN LPCWSTR lpszLocalFileName,
2693     IN FILETIME ExpireTime,
2694     IN FILETIME LastModifiedTime,
2695     IN DWORD CacheEntryType,
2696     IN LPWSTR lpHeaderInfo,
2697     IN DWORD dwHeaderSize,
2698     IN LPCWSTR lpszFileExtension,
2699     IN LPCWSTR lpszOriginalUrl
2700     )
2701 {
2702     DWORD dwError = 0;
2703     BOOL bSuccess = FALSE;
2704     DWORD len = 0;
2705     CHAR *header_info = NULL;
2706
2707     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2708         debugstr_w(lpszUrlName),
2709         debugstr_w(lpszLocalFileName),
2710         CacheEntryType,
2711         lpHeaderInfo,
2712         dwHeaderSize,
2713         debugstr_w(lpszFileExtension),
2714         debugstr_w(lpszOriginalUrl));
2715
2716     if (!lpHeaderInfo ||
2717         ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2718          (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2719     {
2720         if (header_info)
2721             WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2722         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2723                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2724         {
2725                 bSuccess = TRUE;
2726         }
2727         else
2728         {
2729                 dwError = GetLastError();
2730         }
2731         if (header_info)
2732         {
2733             HeapFree(GetProcessHeap(), 0, header_info);
2734             if (!bSuccess)
2735                 SetLastError(dwError);
2736         }
2737     }
2738     return bSuccess;
2739 }
2740
2741 /***********************************************************************
2742  *           ReadUrlCacheEntryStream (WININET.@)
2743  *
2744  */
2745 BOOL WINAPI ReadUrlCacheEntryStream(
2746     IN HANDLE hUrlCacheStream,
2747     IN  DWORD dwLocation,
2748     IN OUT LPVOID lpBuffer,
2749     IN OUT LPDWORD lpdwLen,
2750     IN DWORD dwReserved
2751     )
2752 {
2753     /* Get handle to file from 'stream' */
2754     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2755
2756     if (dwReserved != 0)
2757     {
2758         ERR("dwReserved != 0\n");
2759         SetLastError(ERROR_INVALID_PARAMETER);
2760         return FALSE;
2761     }
2762
2763     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2764     {
2765         SetLastError(ERROR_INVALID_HANDLE);
2766         return FALSE;
2767     }
2768
2769     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2770         return FALSE;
2771     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2772 }
2773
2774 /***********************************************************************
2775  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2776  *
2777  */
2778 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2779     IN LPCSTR lpszUrlName,
2780     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2781     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2782     IN BOOL fRandomRead,
2783     IN DWORD dwReserved
2784     )
2785 {
2786     /* NOTE: this is not the same as the way that the native
2787      * version allocates 'stream' handles. I did it this way
2788      * as it is much easier and no applications should depend
2789      * on this behaviour. (Native version appears to allocate
2790      * indices into a table)
2791      */
2792     STREAM_HANDLE * pStream;
2793     HANDLE hFile;
2794
2795     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2796            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2797
2798     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2799         lpCacheEntryInfo,
2800         lpdwCacheEntryInfoBufferSize,
2801         dwReserved))
2802     {
2803         return NULL;
2804     }
2805
2806     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2807         GENERIC_READ,
2808         FILE_SHARE_READ,
2809         NULL,
2810         OPEN_EXISTING,
2811         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2812         NULL);
2813     if (hFile == INVALID_HANDLE_VALUE)
2814         return FALSE;
2815     
2816     /* allocate handle storage space */
2817     pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2818     if (!pStream)
2819     {
2820         CloseHandle(hFile);
2821         SetLastError(ERROR_OUTOFMEMORY);
2822         return FALSE;
2823     }
2824
2825     pStream->hFile = hFile;
2826     strcpy(pStream->lpszUrl, lpszUrlName);
2827     return pStream;
2828 }
2829
2830 /***********************************************************************
2831  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2832  *
2833  */
2834 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2835     IN LPCWSTR lpszUrlName,
2836     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2837     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2838     IN BOOL fRandomRead,
2839     IN DWORD dwReserved
2840     )
2841 {
2842     FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2843            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2844     return NULL;
2845 }
2846
2847 /***********************************************************************
2848  *           UnlockUrlCacheEntryStream (WININET.@)
2849  *
2850  */
2851 BOOL WINAPI UnlockUrlCacheEntryStream(
2852     IN HANDLE hUrlCacheStream,
2853     IN DWORD dwReserved
2854 )
2855 {
2856     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2857
2858     if (dwReserved != 0)
2859     {
2860         ERR("dwReserved != 0\n");
2861         SetLastError(ERROR_INVALID_PARAMETER);
2862         return FALSE;
2863     }
2864
2865     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2866     {
2867         SetLastError(ERROR_INVALID_HANDLE);
2868         return FALSE;
2869     }
2870
2871     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2872         return FALSE;
2873
2874     /* close file handle */
2875     CloseHandle(pStream->hFile);
2876
2877     /* free allocated space */
2878     HeapFree(GetProcessHeap(), 0, pStream);
2879
2880     return TRUE;
2881 }
2882
2883
2884 /***********************************************************************
2885  *           DeleteUrlCacheEntryA (WININET.@)
2886  *
2887  */
2888 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2889 {
2890     URLCACHECONTAINER * pContainer;
2891     LPURLCACHE_HEADER pHeader;
2892     struct _HASH_ENTRY * pHashEntry;
2893     CACHEFILE_ENTRY * pEntry;
2894     DWORD error;
2895
2896     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2897
2898     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2899     if (error != ERROR_SUCCESS)
2900     {
2901         SetLastError(error);
2902         return FALSE;
2903     }
2904
2905     error = URLCacheContainer_OpenIndex(pContainer);
2906     if (error != ERROR_SUCCESS)
2907     {
2908         SetLastError(error);
2909         return FALSE;
2910     }
2911
2912     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2913         return FALSE;
2914
2915     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2916     {
2917         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2918         TRACE("entry %s not found!\n", lpszUrlName);
2919         SetLastError(ERROR_FILE_NOT_FOUND);
2920         return FALSE;
2921     }
2922
2923     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2924     URLCache_DeleteEntry(pHeader, pEntry);
2925
2926     URLCache_DeleteEntryFromHash(pHashEntry);
2927
2928     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2929
2930     return TRUE;
2931 }
2932
2933 /***********************************************************************
2934  *           DeleteUrlCacheEntryW (WININET.@)
2935  *
2936  */
2937 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2938 {
2939     URLCACHECONTAINER * pContainer;
2940     LPURLCACHE_HEADER pHeader;
2941     struct _HASH_ENTRY * pHashEntry;
2942     CACHEFILE_ENTRY * pEntry;
2943     LPSTR urlA;
2944     int url_len;
2945     DWORD error;
2946
2947     TRACE("(%s)\n", debugstr_w(lpszUrlName));
2948
2949     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2950     urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2951     if (!urlA)
2952     {
2953         SetLastError(ERROR_OUTOFMEMORY);
2954         return FALSE;
2955     }
2956     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2957
2958     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2959     if (error != ERROR_SUCCESS)
2960     {
2961         HeapFree(GetProcessHeap(), 0, urlA);
2962         SetLastError(error);
2963         return FALSE;
2964     }
2965
2966     error = URLCacheContainer_OpenIndex(pContainer);
2967     if (error != ERROR_SUCCESS)
2968     {
2969         HeapFree(GetProcessHeap(), 0, urlA);
2970         SetLastError(error);
2971         return FALSE;
2972     }
2973
2974     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2975     {
2976         HeapFree(GetProcessHeap(), 0, urlA);
2977         return FALSE;
2978     }
2979
2980     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2981     {
2982         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2983         TRACE("entry %s not found!\n", debugstr_a(urlA));
2984         HeapFree(GetProcessHeap(), 0, urlA);
2985         SetLastError(ERROR_FILE_NOT_FOUND);
2986         return FALSE;
2987     }
2988
2989     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2990     URLCache_DeleteEntry(pHeader, pEntry);
2991
2992     URLCache_DeleteEntryFromHash(pHashEntry);
2993
2994     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2995
2996     HeapFree(GetProcessHeap(), 0, urlA);
2997     return TRUE;
2998 }
2999
3000 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3001 {
3002     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3003     return TRUE;
3004 }
3005
3006 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3007 {
3008     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3009     return TRUE;
3010 }
3011
3012 /***********************************************************************
3013  *           CreateCacheContainerA (WININET.@)
3014  */
3015 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3016                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3017 {
3018     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3019           d1, d2, d3, d4, d5, d6, d7, d8);
3020     return TRUE;
3021 }
3022
3023 /***********************************************************************
3024  *           CreateCacheContainerW (WININET.@)
3025  */
3026 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3027                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3028 {
3029     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3030           d1, d2, d3, d4, d5, d6, d7, d8);
3031     return TRUE;
3032 }
3033
3034 /***********************************************************************
3035  *           FindFirstUrlCacheContainerA (WININET.@)
3036  */
3037 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3038 {
3039     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3040     return NULL;
3041 }
3042
3043 /***********************************************************************
3044  *           FindFirstUrlCacheContainerW (WININET.@)
3045  */
3046 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3047 {
3048     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3049     return NULL;
3050 }
3051
3052 /***********************************************************************
3053  *           FindNextUrlCacheContainerA (WININET.@)
3054  */
3055 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3056 {
3057     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3058     return FALSE;
3059 }
3060
3061 /***********************************************************************
3062  *           FindNextUrlCacheContainerW (WININET.@)
3063  */
3064 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3065 {
3066     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3067     return FALSE;
3068 }
3069
3070 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3071   LPCSTR lpszUrlSearchPattern,
3072   DWORD dwFlags,
3073   DWORD dwFilter,
3074   GROUPID GroupId,
3075   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3076   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3077   LPVOID lpReserved,
3078   LPDWORD pcbReserved2,
3079   LPVOID lpReserved3
3080 )
3081 {
3082     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3083           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3084           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3085     SetLastError(ERROR_FILE_NOT_FOUND);
3086     return NULL;
3087 }
3088
3089 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3090   LPCWSTR lpszUrlSearchPattern,
3091   DWORD dwFlags,
3092   DWORD dwFilter,
3093   GROUPID GroupId,
3094   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3095   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3096   LPVOID lpReserved,
3097   LPDWORD pcbReserved2,
3098   LPVOID lpReserved3
3099 )
3100 {
3101     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3102           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3103           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3104     SetLastError(ERROR_FILE_NOT_FOUND);
3105     return NULL;
3106 }
3107
3108 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3109
3110 typedef struct URLCacheFindEntryHandle
3111 {
3112     DWORD dwMagic;
3113     LPWSTR lpszUrlSearchPattern;
3114     DWORD dwContainerIndex;
3115     DWORD dwHashTableIndex;
3116     DWORD dwHashEntryIndex;
3117 } URLCacheFindEntryHandle;
3118
3119 /***********************************************************************
3120  *           FindFirstUrlCacheEntryA (WININET.@)
3121  *
3122  */
3123 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3124  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3125 {
3126     URLCacheFindEntryHandle *pEntryHandle;
3127
3128     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3129
3130     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3131     if (!pEntryHandle)
3132         return NULL;
3133
3134     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3135     if (lpszUrlSearchPattern)
3136     {
3137         int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
3138         pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3139         if (!pEntryHandle->lpszUrlSearchPattern)
3140         {
3141             HeapFree(GetProcessHeap(), 0, pEntryHandle);
3142             return NULL;
3143         }
3144         MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
3145     }
3146     else
3147         pEntryHandle->lpszUrlSearchPattern = NULL;
3148     pEntryHandle->dwContainerIndex = 0;
3149     pEntryHandle->dwHashTableIndex = 0;
3150     pEntryHandle->dwHashEntryIndex = 0;
3151
3152     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3153     {
3154         HeapFree(GetProcessHeap(), 0, pEntryHandle);
3155         return NULL;
3156     }
3157     return pEntryHandle;
3158 }
3159
3160 /***********************************************************************
3161  *           FindFirstUrlCacheEntryW (WININET.@)
3162  *
3163  */
3164 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3165  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3166 {
3167     URLCacheFindEntryHandle *pEntryHandle;
3168
3169     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3170
3171     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3172     if (!pEntryHandle)
3173         return NULL;
3174
3175     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3176     if (lpszUrlSearchPattern)
3177     {
3178         int len = strlenW(lpszUrlSearchPattern);
3179         pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3180         if (!pEntryHandle->lpszUrlSearchPattern)
3181         {
3182             HeapFree(GetProcessHeap(), 0, pEntryHandle);
3183             return NULL;
3184         }
3185         memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3186     }
3187     else
3188         pEntryHandle->lpszUrlSearchPattern = NULL;
3189     pEntryHandle->dwContainerIndex = 0;
3190     pEntryHandle->dwHashTableIndex = 0;
3191     pEntryHandle->dwHashEntryIndex = 0;
3192
3193     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3194     {
3195         HeapFree(GetProcessHeap(), 0, pEntryHandle);
3196         return NULL;
3197     }
3198     return pEntryHandle;
3199 }
3200
3201 /***********************************************************************
3202  *           FindNextUrlCacheEntryA (WININET.@)
3203  */
3204 BOOL WINAPI FindNextUrlCacheEntryA(
3205   HANDLE hEnumHandle,
3206   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3207   LPDWORD lpdwNextCacheEntryInfoBufferSize)
3208 {
3209     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3210     URLCACHECONTAINER * pContainer;
3211
3212     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3213
3214     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3215     {
3216         SetLastError(ERROR_INVALID_HANDLE);
3217         return FALSE;
3218     }
3219
3220     for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3221          pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3222     {
3223         LPURLCACHE_HEADER pHeader;
3224         HASH_CACHEFILE_ENTRY *pHashTableEntry;
3225         DWORD error;
3226
3227         error = URLCacheContainer_OpenIndex(pContainer);
3228         if (error != ERROR_SUCCESS)
3229         {
3230             SetLastError(error);
3231             return FALSE;
3232         }
3233
3234         if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3235             return FALSE;
3236
3237         for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3238              pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3239         {
3240             const struct _HASH_ENTRY *pHashEntry = NULL;
3241             for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3242                  pEntryHandle->dwHashEntryIndex++)
3243             {
3244                 const URL_CACHEFILE_ENTRY *pUrlEntry;
3245                 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3246
3247                 if (pEntry->dwSignature != URL_SIGNATURE)
3248                     continue;
3249
3250                 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3251                 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3252                 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3253
3254                 error = URLCache_CopyEntry(
3255                     pContainer,
3256                     pHeader,
3257                     lpNextCacheEntryInfo,
3258                     lpdwNextCacheEntryInfoBufferSize,
3259                     pUrlEntry,
3260                     FALSE /* not UNICODE */);
3261                 if (error != ERROR_SUCCESS)
3262                 {
3263                     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3264                     SetLastError(error);
3265                     return FALSE;
3266                 }
3267                 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3268
3269                 /* increment the current index so that next time the function
3270                  * is called the next entry is returned */
3271                 pEntryHandle->dwHashEntryIndex++;
3272                 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3273                 return TRUE;
3274             }
3275         }
3276
3277         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3278     }
3279
3280     SetLastError(ERROR_NO_MORE_ITEMS);
3281     return FALSE;
3282 }
3283
3284 /***********************************************************************
3285  *           FindNextUrlCacheEntryW (WININET.@)
3286  */
3287 BOOL WINAPI FindNextUrlCacheEntryW(
3288   HANDLE hEnumHandle,
3289   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3290   LPDWORD lpdwNextCacheEntryInfoBufferSize
3291 )
3292 {
3293     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3294     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3295     return FALSE;
3296 }
3297
3298 /***********************************************************************
3299  *           FindCloseUrlCache (WININET.@)
3300  */
3301 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3302 {
3303     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3304
3305     TRACE("(%p)\n", hEnumHandle);
3306
3307     if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3308     {
3309         SetLastError(ERROR_INVALID_HANDLE);
3310         return FALSE;
3311     }
3312
3313     pEntryHandle->dwMagic = 0;
3314     HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3315     HeapFree(GetProcessHeap(), 0, pEntryHandle);
3316
3317     return TRUE;
3318 }
3319
3320 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3321                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3322 {
3323     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3324           dwSearchCondition, lpGroupId, lpReserved);
3325     return NULL;
3326 }
3327
3328 BOOL WINAPI FindNextUrlCacheEntryExA(
3329   HANDLE hEnumHandle,
3330   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3331   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3332   LPVOID lpReserved,
3333   LPDWORD pcbReserved2,
3334   LPVOID lpReserved3
3335 )
3336 {
3337     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3338           lpReserved, pcbReserved2, lpReserved3);
3339     return FALSE;
3340 }
3341
3342 BOOL WINAPI FindNextUrlCacheEntryExW(
3343   HANDLE hEnumHandle,
3344   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3345   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3346   LPVOID lpReserved,
3347   LPDWORD pcbReserved2,
3348   LPVOID lpReserved3
3349 )
3350 {
3351     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3352           lpReserved, pcbReserved2, lpReserved3);
3353     return FALSE;
3354 }
3355
3356 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3357 {
3358     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3359     return FALSE;
3360 }
3361
3362 /***********************************************************************
3363  *           CreateUrlCacheGroup (WININET.@)
3364  *
3365  */
3366 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3367 {
3368   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3369   return FALSE;
3370 }
3371
3372 /***********************************************************************
3373  *           DeleteUrlCacheGroup (WININET.@)
3374  *
3375  */
3376 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3377 {
3378     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3379           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3380     return FALSE;
3381 }
3382
3383 /***********************************************************************
3384  *           SetUrlCacheEntryGroupA (WININET.@)
3385  *
3386  */
3387 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3388   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3389   LPVOID lpReserved)
3390 {
3391     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3392           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3393           pbGroupAttributes, cbGroupAttributes, lpReserved);
3394     SetLastError(ERROR_FILE_NOT_FOUND);
3395     return FALSE;
3396 }
3397
3398 /***********************************************************************
3399  *           SetUrlCacheEntryGroupW (WININET.@)
3400  *
3401  */
3402 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3403   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3404   LPVOID lpReserved)
3405 {
3406     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3407           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3408           pbGroupAttributes, cbGroupAttributes, lpReserved);
3409     SetLastError(ERROR_FILE_NOT_FOUND);
3410     return FALSE;
3411 }
3412
3413 /***********************************************************************
3414  *           GetUrlCacheConfigInfoW (WININET.@)
3415  */
3416 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3417 {
3418     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3419     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3420     return FALSE;
3421 }
3422
3423 /***********************************************************************
3424  *           GetUrlCacheConfigInfoA (WININET.@)
3425  *
3426  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3427  */
3428 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3429 {
3430     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3431     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3432     return FALSE;
3433 }
3434
3435 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3436                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3437                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3438 {
3439     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3440           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3441           lpdwGroupInfo, lpReserved);
3442     return FALSE;
3443 }
3444
3445 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3446                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3447                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3448 {
3449     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3450           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3451           lpdwGroupInfo, lpReserved);
3452     return FALSE;
3453 }
3454
3455 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3456                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3457 {
3458     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3459           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3460     return TRUE;
3461 }
3462
3463 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3464                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3465 {
3466     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3467           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3468     return TRUE;
3469 }
3470
3471 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3472 {
3473     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3474     return TRUE;
3475 }
3476
3477 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3478 {
3479     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3480     return TRUE;
3481 }
3482
3483 /***********************************************************************
3484  *           DeleteIE3Cache (WININET.@)
3485  *
3486  * Deletes the files used by the IE3 URL caching system.
3487  *
3488  * PARAMS
3489  *   hWnd        [I] A dummy window.
3490  *   hInst       [I] Instance of process calling the function.
3491  *   lpszCmdLine [I] Options used by function.
3492  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
3493  */
3494 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3495 {
3496     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3497     return 0;
3498 }
3499
3500 /***********************************************************************
3501  *           IsUrlCacheEntryExpiredA (WININET.@)
3502  *
3503  * PARAMS
3504  *   url             [I] Url
3505  *   dwFlags         [I] Unknown
3506  *   pftLastModified [O] Last modified time
3507  */
3508 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3509 {
3510     LPURLCACHE_HEADER pHeader;
3511     struct _HASH_ENTRY * pHashEntry;
3512     const CACHEFILE_ENTRY * pEntry;
3513     const URL_CACHEFILE_ENTRY * pUrlEntry;
3514     URLCACHECONTAINER * pContainer;
3515     DWORD error;
3516
3517     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3518
3519     error = URLCacheContainers_FindContainerA(url, &pContainer);
3520     if (error != ERROR_SUCCESS)
3521     {
3522         SetLastError(error);
3523         return FALSE;
3524     }
3525
3526     error = URLCacheContainer_OpenIndex(pContainer);
3527     if (error != ERROR_SUCCESS)
3528     {
3529         SetLastError(error);
3530         return FALSE;
3531     }
3532
3533     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3534         return FALSE;
3535
3536     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3537     {
3538         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3539         TRACE("entry %s not found!\n", url);
3540         SetLastError(ERROR_FILE_NOT_FOUND);
3541         return FALSE;
3542     }
3543
3544     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3545     if (pEntry->dwSignature != URL_SIGNATURE)
3546     {
3547         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3548         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3549         SetLastError(ERROR_FILE_NOT_FOUND);
3550         return FALSE;
3551     }
3552
3553     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3554
3555     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3556
3557     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3558
3559     return TRUE;
3560 }
3561
3562 /***********************************************************************
3563  *           IsUrlCacheEntryExpiredW (WININET.@)
3564  *
3565  * PARAMS
3566  *   url             [I] Url
3567  *   dwFlags         [I] Unknown
3568  *   pftLastModified [O] Last modified time
3569  */
3570 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3571 {
3572     LPURLCACHE_HEADER pHeader;
3573     struct _HASH_ENTRY * pHashEntry;
3574     const CACHEFILE_ENTRY * pEntry;
3575     const URL_CACHEFILE_ENTRY * pUrlEntry;
3576     URLCACHECONTAINER * pContainer;
3577     DWORD error;
3578
3579     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3580
3581     error = URLCacheContainers_FindContainerW(url, &pContainer);
3582     if (error != ERROR_SUCCESS)
3583     {
3584         SetLastError(error);
3585         return FALSE;
3586     }
3587
3588     error = URLCacheContainer_OpenIndex(pContainer);
3589     if (error != ERROR_SUCCESS)
3590     {
3591         SetLastError(error);
3592         return FALSE;
3593     }
3594
3595     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3596         return FALSE;
3597
3598     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3599     {
3600         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3601         TRACE("entry %s not found!\n", debugstr_w(url));
3602         SetLastError(ERROR_FILE_NOT_FOUND);
3603         return FALSE;
3604     }
3605
3606     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3607     if (pEntry->dwSignature != URL_SIGNATURE)
3608     {
3609         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3610         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3611         SetLastError(ERROR_FILE_NOT_FOUND);
3612         return FALSE;
3613     }
3614
3615     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3616
3617     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3618
3619     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3620
3621     return TRUE;
3622 }
3623
3624 /***********************************************************************
3625  *           GetDiskInfoA (WININET.@)
3626  */
3627 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3628 {
3629     BOOL ret;
3630     ULARGE_INTEGER bytes_free, bytes_total;
3631
3632     TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3633
3634     if (!path)
3635     {
3636         SetLastError(ERROR_INVALID_PARAMETER);
3637         return FALSE;
3638     }
3639
3640     if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3641     {
3642         if (cluster_size) *cluster_size = 1;
3643         if (free) *free = bytes_free.QuadPart;
3644         if (total) *total = bytes_total.QuadPart;
3645     }
3646     return ret;
3647 }
3648
3649 /***********************************************************************
3650  *           RegisterUrlCacheNotification (WININET.@)
3651  */
3652 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3653 {
3654     FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3655     return 0;
3656 }
3657
3658 /***********************************************************************
3659  *           IncrementUrlCacheHeaderData (WININET.@)
3660  */
3661 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3662 {
3663     FIXME("(%u, %p)\n", index, data);
3664     return FALSE;
3665 }