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