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