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