server: Implement the file sharing check for delete on close with an existing image...
[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  *           FreeUrlCacheSpaceA (WININET.@)
1453  *
1454  */
1455 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
1456 {
1457     FIXME("stub!\n");
1458     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1459     return FALSE;
1460 }
1461
1462  /***********************************************************************
1463  *           FreeUrlCacheSpaceW (WININET.@)
1464  *
1465  */
1466 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
1467 {
1468     FIXME("stub!\n");
1469     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1470     return FALSE;
1471 }
1472
1473 /***********************************************************************
1474  *           GetUrlCacheEntryInfoExA (WININET.@)
1475  *
1476  */
1477 BOOL WINAPI GetUrlCacheEntryInfoExA(
1478     LPCSTR lpszUrl,
1479     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1480     LPDWORD lpdwCacheEntryInfoBufSize,
1481     LPSTR lpszReserved,
1482     LPDWORD lpdwReserved,
1483     LPVOID lpReserved,
1484     DWORD dwFlags)
1485 {
1486     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1487         debugstr_a(lpszUrl), 
1488         lpCacheEntryInfo,
1489         lpdwCacheEntryInfoBufSize,
1490         lpszReserved,
1491         lpdwReserved,
1492         lpReserved,
1493         dwFlags);
1494
1495     if ((lpszReserved != NULL) ||
1496         (lpdwReserved != NULL) ||
1497         (lpReserved != NULL))
1498     {
1499         ERR("Reserved value was not 0\n");
1500         SetLastError(ERROR_INVALID_PARAMETER);
1501         return FALSE;
1502     }
1503     if (dwFlags != 0)
1504     {
1505         FIXME("Undocumented flag(s): %x\n", dwFlags);
1506         SetLastError(ERROR_FILE_NOT_FOUND);
1507         return FALSE;
1508     }
1509     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1510 }
1511
1512 /***********************************************************************
1513  *           GetUrlCacheEntryInfoA (WININET.@)
1514  *
1515  */
1516 BOOL WINAPI GetUrlCacheEntryInfoA(
1517     IN LPCSTR lpszUrlName,
1518     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1519     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1520 )
1521 {
1522     LPURLCACHE_HEADER pHeader;
1523     struct _HASH_ENTRY * pHashEntry;
1524     const CACHEFILE_ENTRY * pEntry;
1525     const URL_CACHEFILE_ENTRY * pUrlEntry;
1526     URLCACHECONTAINER * pContainer;
1527     DWORD error;
1528
1529     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1530
1531     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1532     if (error != ERROR_SUCCESS)
1533     {
1534         SetLastError(error);
1535         return FALSE;
1536     }
1537
1538     error = URLCacheContainer_OpenIndex(pContainer);
1539     if (error != ERROR_SUCCESS)
1540     {
1541         SetLastError(error);
1542         return FALSE;
1543     }
1544
1545     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1546         return FALSE;
1547
1548     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1549     {
1550         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1551         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1552         SetLastError(ERROR_FILE_NOT_FOUND);
1553         return FALSE;
1554     }
1555
1556     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1557     if (pEntry->dwSignature != URL_SIGNATURE)
1558     {
1559         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1560         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1561         SetLastError(ERROR_FILE_NOT_FOUND);
1562         return FALSE;
1563     }
1564
1565     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1566     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1567     if (pUrlEntry->dwOffsetHeaderInfo)
1568         TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1569
1570     if (lpdwCacheEntryInfoBufferSize)
1571     {
1572         if (!lpCacheEntryInfo)
1573             *lpdwCacheEntryInfoBufferSize = 0;
1574
1575         error = URLCache_CopyEntry(
1576             pContainer,
1577             pHeader,
1578             lpCacheEntryInfo,
1579             lpdwCacheEntryInfoBufferSize,
1580             pUrlEntry,
1581             FALSE /* ANSI */);
1582         if (error != ERROR_SUCCESS)
1583         {
1584             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1585             SetLastError(error);
1586             return FALSE;
1587         }
1588         TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1589     }
1590
1591     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1592
1593     return TRUE;
1594 }
1595
1596 /***********************************************************************
1597  *           GetUrlCacheEntryInfoW (WININET.@)
1598  *
1599  */
1600 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1601   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1602   LPDWORD lpdwCacheEntryInfoBufferSize)
1603 {
1604     LPURLCACHE_HEADER pHeader;
1605     struct _HASH_ENTRY * pHashEntry;
1606     const CACHEFILE_ENTRY * pEntry;
1607     const URL_CACHEFILE_ENTRY * pUrlEntry;
1608     URLCACHECONTAINER * pContainer;
1609     DWORD error;
1610
1611     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1612
1613     error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1614     if (error != ERROR_SUCCESS)
1615     {
1616         SetLastError(error);
1617         return FALSE;
1618     }
1619
1620     error = URLCacheContainer_OpenIndex(pContainer);
1621     if (error != ERROR_SUCCESS)
1622     {
1623         SetLastError(error);
1624         return FALSE;
1625     }
1626
1627     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1628         return FALSE;
1629
1630     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1631     {
1632         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1633         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1634         SetLastError(ERROR_FILE_NOT_FOUND);
1635         return FALSE;
1636     }
1637
1638     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1639     if (pEntry->dwSignature != URL_SIGNATURE)
1640     {
1641         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1642         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1643         SetLastError(ERROR_FILE_NOT_FOUND);
1644         return FALSE;
1645     }
1646
1647     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1648     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1649     TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1650
1651     if (lpdwCacheEntryInfoBufferSize)
1652     {
1653         if (!lpCacheEntryInfo)
1654             *lpdwCacheEntryInfoBufferSize = 0;
1655
1656         error = URLCache_CopyEntry(
1657             pContainer,
1658             pHeader,
1659             (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1660             lpdwCacheEntryInfoBufferSize,
1661             pUrlEntry,
1662             TRUE /* UNICODE */);
1663         if (error != ERROR_SUCCESS)
1664         {
1665             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1666             SetLastError(error);
1667             return FALSE;
1668         }
1669         TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1670     }
1671
1672     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1673
1674     return TRUE;
1675 }
1676
1677 /***********************************************************************
1678  *           GetUrlCacheEntryInfoExW (WININET.@)
1679  *
1680  */
1681 BOOL WINAPI GetUrlCacheEntryInfoExW(
1682     LPCWSTR lpszUrl,
1683     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1684     LPDWORD lpdwCacheEntryInfoBufSize,
1685     LPWSTR lpszReserved,
1686     LPDWORD lpdwReserved,
1687     LPVOID lpReserved,
1688     DWORD dwFlags)
1689 {
1690     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1691         debugstr_w(lpszUrl), 
1692         lpCacheEntryInfo,
1693         lpdwCacheEntryInfoBufSize,
1694         lpszReserved,
1695         lpdwReserved,
1696         lpReserved,
1697         dwFlags);
1698
1699     if ((lpszReserved != NULL) ||
1700         (lpdwReserved != NULL) ||
1701         (lpReserved != NULL))
1702     {
1703         ERR("Reserved value was not 0\n");
1704         SetLastError(ERROR_INVALID_PARAMETER);
1705         return FALSE;
1706     }
1707     if (dwFlags != 0)
1708     {
1709         FIXME("Undocumented flag(s): %x\n", dwFlags);
1710         SetLastError(ERROR_FILE_NOT_FOUND);
1711         return FALSE;
1712     }
1713     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1714 }
1715
1716 /***********************************************************************
1717  *           SetUrlCacheEntryInfoA (WININET.@)
1718  */
1719 BOOL WINAPI SetUrlCacheEntryInfoA(
1720     LPCSTR lpszUrlName,
1721     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1722     DWORD dwFieldControl)
1723 {
1724     LPURLCACHE_HEADER pHeader;
1725     struct _HASH_ENTRY * pHashEntry;
1726     CACHEFILE_ENTRY * pEntry;
1727     URLCACHECONTAINER * pContainer;
1728     DWORD error;
1729
1730     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1731
1732     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1733     if (error != ERROR_SUCCESS)
1734     {
1735         SetLastError(error);
1736         return FALSE;
1737     }
1738
1739     error = URLCacheContainer_OpenIndex(pContainer);
1740     if (error != ERROR_SUCCESS)
1741     {
1742         SetLastError(error);
1743         return FALSE;
1744     }
1745
1746     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1747         return FALSE;
1748
1749     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1750     {
1751         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1752         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1753         SetLastError(ERROR_FILE_NOT_FOUND);
1754         return FALSE;
1755     }
1756
1757     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1758     if (pEntry->dwSignature != URL_SIGNATURE)
1759     {
1760         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1761         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1762         SetLastError(ERROR_FILE_NOT_FOUND);
1763         return FALSE;
1764     }
1765
1766     URLCache_SetEntryInfo(
1767         (URL_CACHEFILE_ENTRY *)pEntry,
1768         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1769         dwFieldControl);
1770
1771     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1772
1773     return TRUE;
1774 }
1775
1776 /***********************************************************************
1777  *           SetUrlCacheEntryInfoW (WININET.@)
1778  */
1779 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1780 {
1781     LPURLCACHE_HEADER pHeader;
1782     struct _HASH_ENTRY * pHashEntry;
1783     CACHEFILE_ENTRY * pEntry;
1784     URLCACHECONTAINER * pContainer;
1785     DWORD error;
1786
1787     TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1788
1789     error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1790     if (error != ERROR_SUCCESS)
1791     {
1792         SetLastError(error);
1793         return FALSE;
1794     }
1795
1796     error = URLCacheContainer_OpenIndex(pContainer);
1797     if (error != ERROR_SUCCESS)
1798     {
1799         SetLastError(error);
1800         return FALSE;
1801     }
1802
1803     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1804         return FALSE;
1805
1806     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1807     {
1808         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1809         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1810         SetLastError(ERROR_FILE_NOT_FOUND);
1811         return FALSE;
1812     }
1813
1814     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1815     if (pEntry->dwSignature != URL_SIGNATURE)
1816     {
1817         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1818         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1819         SetLastError(ERROR_FILE_NOT_FOUND);
1820         return FALSE;
1821     }
1822
1823     URLCache_SetEntryInfo(
1824         (URL_CACHEFILE_ENTRY *)pEntry,
1825         lpCacheEntryInfo,
1826         dwFieldControl);
1827
1828     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1829
1830     return TRUE;
1831 }
1832
1833 /***********************************************************************
1834  *           RetrieveUrlCacheEntryFileA (WININET.@)
1835  *
1836  */
1837 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1838     IN LPCSTR lpszUrlName,
1839     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1840     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1841     IN DWORD dwReserved
1842     )
1843 {
1844     LPURLCACHE_HEADER pHeader;
1845     struct _HASH_ENTRY * pHashEntry;
1846     CACHEFILE_ENTRY * pEntry;
1847     URL_CACHEFILE_ENTRY * pUrlEntry;
1848     URLCACHECONTAINER * pContainer;
1849     DWORD error;
1850
1851     TRACE("(%s, %p, %p, 0x%08x)\n",
1852         debugstr_a(lpszUrlName),
1853         lpCacheEntryInfo,
1854         lpdwCacheEntryInfoBufferSize,
1855         dwReserved);
1856
1857     if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1858         (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1859     {
1860         SetLastError(ERROR_INVALID_PARAMETER);
1861         return FALSE;
1862     }
1863
1864     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1865     if (error != ERROR_SUCCESS)
1866     {
1867         SetLastError(error);
1868         return FALSE;
1869     }
1870
1871     error = URLCacheContainer_OpenIndex(pContainer);
1872     if (error != ERROR_SUCCESS)
1873     {
1874         SetLastError(error);
1875         return FALSE;
1876     }
1877
1878     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1879         return FALSE;
1880
1881     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1882     {
1883         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1884         TRACE("entry %s not found!\n", lpszUrlName);
1885         SetLastError(ERROR_FILE_NOT_FOUND);
1886         return FALSE;
1887     }
1888
1889     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1890     if (pEntry->dwSignature != URL_SIGNATURE)
1891     {
1892         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1893         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1894         SetLastError(ERROR_FILE_NOT_FOUND);
1895         return FALSE;
1896     }
1897
1898     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1899     if (!pUrlEntry->dwOffsetLocalName)
1900     {
1901         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1902         SetLastError(ERROR_INVALID_DATA);
1903         return FALSE;
1904     }
1905
1906     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1907     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1908
1909     pUrlEntry->dwHitRate++;
1910     pUrlEntry->dwUseCount++;
1911     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1912
1913     error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
1914                                lpdwCacheEntryInfoBufferSize, pUrlEntry,
1915                                FALSE);
1916     if (error != ERROR_SUCCESS)
1917     {
1918         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1919         SetLastError(error);
1920         return FALSE;
1921     }
1922     TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1923
1924     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1925
1926     return TRUE;
1927 }
1928
1929 /***********************************************************************
1930  *           RetrieveUrlCacheEntryFileW (WININET.@)
1931  *
1932  */
1933 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1934     IN LPCWSTR lpszUrlName,
1935     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1936     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1937     IN DWORD dwReserved
1938     )
1939 {
1940     LPURLCACHE_HEADER pHeader;
1941     struct _HASH_ENTRY * pHashEntry;
1942     CACHEFILE_ENTRY * pEntry;
1943     URL_CACHEFILE_ENTRY * pUrlEntry;
1944     URLCACHECONTAINER * pContainer;
1945     DWORD error;
1946
1947     TRACE("(%s, %p, %p, 0x%08x)\n",
1948         debugstr_w(lpszUrlName),
1949         lpCacheEntryInfo,
1950         lpdwCacheEntryInfoBufferSize,
1951         dwReserved);
1952
1953     if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1954         (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1955     {
1956         SetLastError(ERROR_INVALID_PARAMETER);
1957         return FALSE;
1958     }
1959
1960     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
1961     if (error != ERROR_SUCCESS)
1962     {
1963         SetLastError(error);
1964         return FALSE;
1965     }
1966
1967     error = URLCacheContainer_OpenIndex(pContainer);
1968     if (error != ERROR_SUCCESS)
1969     {
1970         SetLastError(error);
1971         return FALSE;
1972     }
1973
1974     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1975         return FALSE;
1976
1977     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1978     {
1979         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1980         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1981         SetLastError(ERROR_FILE_NOT_FOUND);
1982         return FALSE;
1983     }
1984
1985     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1986     if (pEntry->dwSignature != URL_SIGNATURE)
1987     {
1988         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1989         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1990         SetLastError(ERROR_FILE_NOT_FOUND);
1991         return FALSE;
1992     }
1993
1994     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1995     if (!pUrlEntry->dwOffsetLocalName)
1996     {
1997         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1998         SetLastError(ERROR_INVALID_DATA);
1999         return FALSE;
2000     }
2001
2002     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2003     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2004
2005     pUrlEntry->dwHitRate++;
2006     pUrlEntry->dwUseCount++;
2007     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2008
2009     error = URLCache_CopyEntry(
2010         pContainer,
2011         pHeader,
2012         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2013         lpdwCacheEntryInfoBufferSize,
2014         pUrlEntry,
2015         TRUE /* UNICODE */);
2016     if (error != ERROR_SUCCESS)
2017     {
2018         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2019         SetLastError(error);
2020         return FALSE;
2021     }
2022     TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2023
2024     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2025
2026     return TRUE;
2027 }
2028
2029 /***********************************************************************
2030  *           UnlockUrlCacheEntryFileA (WININET.@)
2031  *
2032  */
2033 BOOL WINAPI UnlockUrlCacheEntryFileA(
2034     IN LPCSTR lpszUrlName, 
2035     IN DWORD dwReserved
2036     )
2037 {
2038     LPURLCACHE_HEADER pHeader;
2039     struct _HASH_ENTRY * pHashEntry;
2040     CACHEFILE_ENTRY * pEntry;
2041     URL_CACHEFILE_ENTRY * pUrlEntry;
2042     URLCACHECONTAINER * pContainer;
2043     DWORD error;
2044
2045     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2046
2047     if (dwReserved)
2048     {
2049         ERR("dwReserved != 0\n");
2050         SetLastError(ERROR_INVALID_PARAMETER);
2051         return FALSE;
2052     }
2053
2054     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2055     if (error != ERROR_SUCCESS)
2056     {
2057        SetLastError(error);
2058        return FALSE;
2059     }
2060
2061     error = URLCacheContainer_OpenIndex(pContainer);
2062     if (error != ERROR_SUCCESS)
2063     {
2064         SetLastError(error);
2065         return FALSE;
2066     }
2067
2068     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2069         return FALSE;
2070
2071     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2072     {
2073         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2074         TRACE("entry %s not found!\n", lpszUrlName);
2075         SetLastError(ERROR_FILE_NOT_FOUND);
2076         return FALSE;
2077     }
2078
2079     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2080     if (pEntry->dwSignature != URL_SIGNATURE)
2081     {
2082         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2083         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2084         SetLastError(ERROR_FILE_NOT_FOUND);
2085         return FALSE;
2086     }
2087
2088     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2089
2090     if (pUrlEntry->dwUseCount == 0)
2091     {
2092         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2093         return FALSE;
2094     }
2095     pUrlEntry->dwUseCount--;
2096     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2097
2098     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2099
2100     return TRUE;
2101 }
2102
2103 /***********************************************************************
2104  *           UnlockUrlCacheEntryFileW (WININET.@)
2105  *
2106  */
2107 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2108 {
2109     LPURLCACHE_HEADER pHeader;
2110     struct _HASH_ENTRY * pHashEntry;
2111     CACHEFILE_ENTRY * pEntry;
2112     URL_CACHEFILE_ENTRY * pUrlEntry;
2113     URLCACHECONTAINER * pContainer;
2114     DWORD error;
2115
2116     TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2117
2118     if (dwReserved)
2119     {
2120         ERR("dwReserved != 0\n");
2121         SetLastError(ERROR_INVALID_PARAMETER);
2122         return FALSE;
2123     }
2124
2125     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2126     if (error != ERROR_SUCCESS)
2127     {
2128         SetLastError(error);
2129         return FALSE;
2130     }
2131
2132     error = URLCacheContainer_OpenIndex(pContainer);
2133     if (error != ERROR_SUCCESS)
2134     {
2135         SetLastError(error);
2136         return FALSE;
2137     }
2138
2139     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2140         return FALSE;
2141
2142     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2143     {
2144         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2145         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2146         SetLastError(ERROR_FILE_NOT_FOUND);
2147         return FALSE;
2148     }
2149
2150     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2151     if (pEntry->dwSignature != URL_SIGNATURE)
2152     {
2153         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2154         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2155         SetLastError(ERROR_FILE_NOT_FOUND);
2156         return FALSE;
2157     }
2158
2159     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2160
2161     if (pUrlEntry->dwUseCount == 0)
2162     {
2163         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2164         return FALSE;
2165     }
2166     pUrlEntry->dwUseCount--;
2167     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2168
2169     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2170
2171     return TRUE;
2172 }
2173
2174 /***********************************************************************
2175  *           CreateUrlCacheEntryA (WININET.@)
2176  *
2177  */
2178 BOOL WINAPI CreateUrlCacheEntryA(
2179     IN LPCSTR lpszUrlName,
2180     IN DWORD dwExpectedFileSize,
2181     IN LPCSTR lpszFileExtension,
2182     OUT LPSTR lpszFileName,
2183     IN DWORD dwReserved
2184 )
2185 {
2186     WCHAR *url_name;
2187     WCHAR *file_extension;
2188     WCHAR file_name[MAX_PATH];
2189     BOOL bSuccess = FALSE;
2190     DWORD dwError = 0;
2191
2192     if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2193     {
2194         if (lpszFileExtension && (file_extension = heap_strdupAtoW(lpszFileExtension)))
2195         {
2196             if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2197             {
2198                 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2199                 {
2200                     bSuccess = TRUE;
2201                 }
2202                 else
2203                 {
2204                     dwError = GetLastError();
2205                 }
2206             }
2207             else
2208             {
2209                 dwError = GetLastError();
2210             }
2211             HeapFree(GetProcessHeap(), 0, file_extension);
2212         }
2213         else
2214         {
2215             dwError = GetLastError();
2216         }
2217         HeapFree(GetProcessHeap(), 0, url_name);
2218         if (!bSuccess)
2219             SetLastError(dwError);
2220     }
2221     return bSuccess;
2222 }
2223 /***********************************************************************
2224  *           CreateUrlCacheEntryW (WININET.@)
2225  *
2226  */
2227 BOOL WINAPI CreateUrlCacheEntryW(
2228     IN LPCWSTR lpszUrlName,
2229     IN DWORD dwExpectedFileSize,
2230     IN LPCWSTR lpszFileExtension,
2231     OUT LPWSTR lpszFileName,
2232     IN DWORD dwReserved
2233 )
2234 {
2235     URLCACHECONTAINER * pContainer;
2236     LPURLCACHE_HEADER pHeader;
2237     CHAR szFile[MAX_PATH];
2238     WCHAR szExtension[MAX_PATH];
2239     LPCWSTR lpszUrlPart;
2240     LPCWSTR lpszUrlEnd;
2241     LPCWSTR lpszFileNameExtension;
2242     LPWSTR lpszFileNameNoPath;
2243     int i;
2244     int countnoextension;
2245     BYTE CacheDir;
2246     LONG lBufferSize;
2247     BOOL bFound = FALSE;
2248     int count;
2249     DWORD error;
2250     HANDLE hFile;
2251     FILETIME ft;
2252
2253     static const WCHAR szWWW[] = {'w','w','w',0};
2254     static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2255
2256     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2257         debugstr_w(lpszUrlName),
2258         dwExpectedFileSize,
2259         debugstr_w(lpszFileExtension),
2260         lpszFileName,
2261         dwReserved);
2262
2263     if (dwReserved)
2264         FIXME("dwReserved 0x%08x\n", dwReserved);
2265
2266    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2267     
2268     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2269         lpszUrlEnd--;
2270
2271     for (lpszUrlPart = lpszUrlEnd; 
2272         (lpszUrlPart >= lpszUrlName); 
2273         lpszUrlPart--)
2274     {
2275         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2276         {
2277             bFound = TRUE;
2278             lpszUrlPart++;
2279             break;
2280         }
2281         else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2282         {
2283             lpszUrlEnd = lpszUrlPart;
2284         }
2285     }
2286     if (!lstrcmpW(lpszUrlPart, szWWW))
2287     {
2288         lpszUrlPart += lstrlenW(szWWW);
2289     }
2290
2291     count = lpszUrlEnd - lpszUrlPart;
2292
2293     if (bFound && (count < MAX_PATH))
2294     {
2295         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2296         if (!len)
2297             return FALSE;
2298         szFile[len] = '\0';
2299         while(len && szFile[--len] == '/') szFile[len] = '\0';
2300
2301         /* FIXME: get rid of illegal characters like \, / and : */
2302     }
2303     else
2304     {
2305         FIXME("need to generate a random filename\n");
2306     }
2307
2308     TRACE("File name: %s\n", debugstr_a(szFile));
2309
2310     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2311     if (error != ERROR_SUCCESS)
2312     {
2313         SetLastError(error);
2314         return FALSE;
2315     }
2316
2317     error = URLCacheContainer_OpenIndex(pContainer);
2318     if (error != ERROR_SUCCESS)
2319     {
2320         SetLastError(error);
2321         return FALSE;
2322     }
2323
2324     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2325         return FALSE;
2326
2327     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2328
2329     lBufferSize = MAX_PATH * sizeof(WCHAR);
2330     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2331
2332     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2333
2334     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2335         lpszFileNameNoPath >= lpszFileName; 
2336         --lpszFileNameNoPath)
2337     {
2338         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2339             break;
2340     }
2341
2342     countnoextension = lstrlenW(lpszFileNameNoPath);
2343     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2344     if (lpszFileNameExtension)
2345         countnoextension -= lstrlenW(lpszFileNameExtension);
2346     *szExtension = '\0';
2347
2348     if (lpszFileExtension)
2349     {
2350         szExtension[0] = '.';
2351         lstrcpyW(szExtension+1, lpszFileExtension);
2352     }
2353
2354     for (i = 0; i < 255; i++)
2355     {
2356         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2357         WCHAR *p;
2358
2359         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2360         for (p = lpszFileNameNoPath + 1; *p; p++)
2361         {
2362             switch (*p)
2363             {
2364             case '<': case '>':
2365             case ':': case '"':
2366             case '/': case '\\':
2367             case '|': case '?':
2368             case '*':
2369                 *p = '_'; break;
2370             default: break;
2371             }
2372         }
2373         if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2374
2375         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2376         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2377         if (hFile != INVALID_HANDLE_VALUE)
2378         {
2379             CloseHandle(hFile);
2380             return TRUE;
2381         }
2382     }
2383
2384     GetSystemTimeAsFileTime(&ft);
2385     wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2386
2387     TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2388     hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2389     if (hFile != INVALID_HANDLE_VALUE)
2390     {
2391         CloseHandle(hFile);
2392         return TRUE;
2393     }
2394
2395     WARN("Could not find a unique filename\n");
2396     return FALSE;
2397 }
2398
2399
2400 /***********************************************************************
2401  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2402  *
2403  *   The bug we are compensating for is that some drongo at Microsoft
2404  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2405  *   As a consequence, CommitUrlCacheEntryA has been effectively
2406  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2407  *   is still defined as LPCWSTR. The result (other than madness) is
2408  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2409  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2410  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2411  *   result will lose data for arbitrary binary data.
2412  *
2413  */
2414 static BOOL CommitUrlCacheEntryInternal(
2415     IN LPCWSTR lpszUrlName,
2416     IN LPCWSTR lpszLocalFileName,
2417     IN FILETIME ExpireTime,
2418     IN FILETIME LastModifiedTime,
2419     IN DWORD CacheEntryType,
2420     IN LPBYTE lpHeaderInfo,
2421     IN DWORD dwHeaderSize,
2422     IN LPCWSTR lpszFileExtension,
2423     IN LPCWSTR lpszOriginalUrl
2424     )
2425 {
2426     URLCACHECONTAINER * pContainer;
2427     LPURLCACHE_HEADER pHeader;
2428     struct _HASH_ENTRY * pHashEntry;
2429     CACHEFILE_ENTRY * pEntry;
2430     URL_CACHEFILE_ENTRY * pUrlEntry;
2431     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2432     DWORD dwOffsetLocalFileName = 0;
2433     DWORD dwOffsetHeader = 0;
2434     DWORD dwOffsetFileExtension = 0;
2435     DWORD dwFileSizeLow = 0;
2436     DWORD dwFileSizeHigh = 0;
2437     BYTE cDirectory = 0;
2438     int len;
2439     char achFile[MAX_PATH];
2440     LPSTR lpszUrlNameA = NULL;
2441     LPSTR lpszFileExtensionA = NULL;
2442     char *pchLocalFileName = 0;
2443     DWORD error;
2444
2445     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2446         debugstr_w(lpszUrlName),
2447         debugstr_w(lpszLocalFileName),
2448         CacheEntryType,
2449         lpHeaderInfo,
2450         dwHeaderSize,
2451         debugstr_w(lpszFileExtension),
2452         debugstr_w(lpszOriginalUrl));
2453
2454     if (lpszOriginalUrl)
2455         WARN(": lpszOriginalUrl ignored\n");
2456  
2457     if (lpszLocalFileName)
2458     {
2459         HANDLE hFile;
2460
2461         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2462         if (hFile == INVALID_HANDLE_VALUE)
2463         {
2464             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2465             return FALSE;
2466         }
2467
2468         /* Get file size */
2469         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2470         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2471         {
2472             ERR("couldn't get file size (error is %d)\n", GetLastError());
2473             CloseHandle(hFile);
2474             return FALSE;
2475         }
2476
2477         CloseHandle(hFile);
2478     }
2479
2480     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2481     if (error != ERROR_SUCCESS)
2482     {
2483         SetLastError(error);
2484         return FALSE;
2485     }
2486
2487     error = URLCacheContainer_OpenIndex(pContainer);
2488     if (error != ERROR_SUCCESS)
2489     {
2490         SetLastError(error);
2491         return FALSE;
2492     }
2493
2494     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2495         return FALSE;
2496
2497     lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2498     if (!lpszUrlNameA)
2499     {
2500         error = GetLastError();
2501         goto cleanup;
2502     }
2503
2504     if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2505     {
2506         error = GetLastError();
2507         goto cleanup;
2508     }
2509
2510     if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2511     {
2512         FIXME("entry already in cache - don't know what to do!\n");
2513 /*
2514  *        SetLastError(ERROR_FILE_NOT_FOUND);
2515  *        return FALSE;
2516  */
2517         goto cleanup;
2518     }
2519
2520     if (lpszLocalFileName)
2521     {
2522         BOOL bFound = FALSE;
2523
2524         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2525         {
2526             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2527             error = ERROR_INVALID_PARAMETER;
2528             goto cleanup;
2529         }
2530
2531         /* skip container path prefix */
2532         lpszLocalFileName += lstrlenW(pContainer->path);
2533
2534         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2535         pchLocalFileName = achFile;
2536
2537         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2538         {
2539             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2540             {
2541                 bFound = TRUE;
2542                 break;
2543             }
2544         }
2545
2546         if (!bFound)
2547         {
2548             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2549             error = ERROR_INVALID_PARAMETER;
2550             goto cleanup;
2551         }
2552
2553         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2554     }
2555
2556     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2557     if (lpszLocalFileName)
2558     {
2559         len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2560         dwOffsetLocalFileName = dwBytesNeeded;
2561         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2562     }
2563     if (lpHeaderInfo)
2564     {
2565         dwOffsetHeader = dwBytesNeeded;
2566         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2567     }
2568     if (lpszFileExtensionA)
2569     {
2570         dwOffsetFileExtension = dwBytesNeeded;
2571         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2572     }
2573
2574     /* round up to next block */
2575     if (dwBytesNeeded % BLOCKSIZE)
2576     {
2577         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2578         dwBytesNeeded += BLOCKSIZE;
2579     }
2580
2581     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2582     {
2583         ERR("no free entries\n");
2584         error = ERROR_DISK_FULL;
2585         goto cleanup;
2586     }
2587
2588     /* FindFirstFreeEntry fills in blocks used */
2589     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2590     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2591     pUrlEntry->CacheDir = cDirectory;
2592     pUrlEntry->CacheEntryType = CacheEntryType;
2593     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2594     pUrlEntry->dwExemptDelta = 0;
2595     pUrlEntry->dwHitRate = 0;
2596     pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2597     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2598     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2599     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2600     pUrlEntry->dwSizeHigh = 0;
2601     pUrlEntry->dwSizeLow = dwFileSizeLow;
2602     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2603     pUrlEntry->dwUseCount = 0;
2604     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2605     pUrlEntry->LastModifiedTime = LastModifiedTime;
2606     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2607     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2608     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2609     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2610
2611     /*** Unknowns ***/
2612     pUrlEntry->dwUnknown1 = 0;
2613     pUrlEntry->dwUnknown2 = 0;
2614     pUrlEntry->dwUnknown3 = 0x60;
2615     pUrlEntry->Unknown4 = 0;
2616     pUrlEntry->wUnknown5 = 0x1010;
2617     pUrlEntry->dwUnknown7 = 0;
2618     pUrlEntry->dwUnknown8 = 0;
2619
2620
2621     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2622     if (dwOffsetLocalFileName)
2623         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2624     if (dwOffsetHeader)
2625         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2626     if (dwOffsetFileExtension)
2627         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2628
2629     error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2630         (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2631     if (error != ERROR_SUCCESS)
2632         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2633
2634 cleanup:
2635     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2636     HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2637     HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2638
2639     if (error == ERROR_SUCCESS)
2640         return TRUE;
2641     else
2642     {
2643         SetLastError(error);
2644         return FALSE;
2645     }
2646 }
2647
2648 /***********************************************************************
2649  *           CommitUrlCacheEntryA (WININET.@)
2650  *
2651  */
2652 BOOL WINAPI CommitUrlCacheEntryA(
2653     IN LPCSTR lpszUrlName,
2654     IN LPCSTR lpszLocalFileName,
2655     IN FILETIME ExpireTime,
2656     IN FILETIME LastModifiedTime,
2657     IN DWORD CacheEntryType,
2658     IN LPBYTE lpHeaderInfo,
2659     IN DWORD dwHeaderSize,
2660     IN LPCSTR lpszFileExtension,
2661     IN LPCSTR lpszOriginalUrl
2662     )
2663 {
2664     WCHAR *url_name = NULL;
2665     WCHAR *local_file_name = NULL;
2666     WCHAR *original_url = NULL;
2667     WCHAR *file_extension = NULL;
2668     BOOL bSuccess = FALSE;
2669
2670     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2671         debugstr_a(lpszUrlName),
2672         debugstr_a(lpszLocalFileName),
2673         CacheEntryType,
2674         lpHeaderInfo,
2675         dwHeaderSize,
2676         debugstr_a(lpszFileExtension),
2677         debugstr_a(lpszOriginalUrl));
2678
2679     url_name = heap_strdupAtoW(lpszUrlName);
2680     if (!url_name)
2681         goto cleanup;
2682
2683     if (lpszLocalFileName)
2684     {
2685         local_file_name = heap_strdupAtoW(lpszLocalFileName);
2686         if (!local_file_name)
2687             goto cleanup;
2688     }
2689     if (lpszFileExtension)
2690     {
2691         file_extension = heap_strdupAtoW(lpszFileExtension);
2692         if (!file_extension)
2693             goto cleanup;
2694     }
2695     if (lpszOriginalUrl)
2696     {
2697         original_url = heap_strdupAtoW(lpszOriginalUrl);
2698         if (!original_url)
2699             goto cleanup;
2700     }
2701
2702     bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2703                                            CacheEntryType, lpHeaderInfo, dwHeaderSize,
2704                                            file_extension, original_url);
2705
2706 cleanup:
2707     HeapFree(GetProcessHeap(), 0, original_url);
2708     HeapFree(GetProcessHeap(), 0, file_extension);
2709     HeapFree(GetProcessHeap(), 0, local_file_name);
2710     HeapFree(GetProcessHeap(), 0, url_name);
2711
2712     return bSuccess;
2713 }
2714
2715 /***********************************************************************
2716  *           CommitUrlCacheEntryW (WININET.@)
2717  *
2718  */
2719 BOOL WINAPI CommitUrlCacheEntryW(
2720     IN LPCWSTR lpszUrlName,
2721     IN LPCWSTR lpszLocalFileName,
2722     IN FILETIME ExpireTime,
2723     IN FILETIME LastModifiedTime,
2724     IN DWORD CacheEntryType,
2725     IN LPWSTR lpHeaderInfo,
2726     IN DWORD dwHeaderSize,
2727     IN LPCWSTR lpszFileExtension,
2728     IN LPCWSTR lpszOriginalUrl
2729     )
2730 {
2731     DWORD dwError = 0;
2732     BOOL bSuccess = FALSE;
2733     DWORD len = 0;
2734     CHAR *header_info = NULL;
2735
2736     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2737         debugstr_w(lpszUrlName),
2738         debugstr_w(lpszLocalFileName),
2739         CacheEntryType,
2740         lpHeaderInfo,
2741         dwHeaderSize,
2742         debugstr_w(lpszFileExtension),
2743         debugstr_w(lpszOriginalUrl));
2744
2745     if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2746     {
2747         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2748                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2749         {
2750                 bSuccess = TRUE;
2751         }
2752         else
2753         {
2754                 dwError = GetLastError();
2755         }
2756         if (header_info)
2757         {
2758             HeapFree(GetProcessHeap(), 0, header_info);
2759             if (!bSuccess)
2760                 SetLastError(dwError);
2761         }
2762     }
2763     return bSuccess;
2764 }
2765
2766 /***********************************************************************
2767  *           ReadUrlCacheEntryStream (WININET.@)
2768  *
2769  */
2770 BOOL WINAPI ReadUrlCacheEntryStream(
2771     IN HANDLE hUrlCacheStream,
2772     IN  DWORD dwLocation,
2773     IN OUT LPVOID lpBuffer,
2774     IN OUT LPDWORD lpdwLen,
2775     IN DWORD dwReserved
2776     )
2777 {
2778     /* Get handle to file from 'stream' */
2779     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2780
2781     if (dwReserved != 0)
2782     {
2783         ERR("dwReserved != 0\n");
2784         SetLastError(ERROR_INVALID_PARAMETER);
2785         return FALSE;
2786     }
2787
2788     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2789     {
2790         SetLastError(ERROR_INVALID_HANDLE);
2791         return FALSE;
2792     }
2793
2794     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2795         return FALSE;
2796     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2797 }
2798
2799 /***********************************************************************
2800  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2801  *
2802  */
2803 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2804     IN LPCSTR lpszUrlName,
2805     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2806     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2807     IN BOOL fRandomRead,
2808     IN DWORD dwReserved
2809     )
2810 {
2811     /* NOTE: this is not the same as the way that the native
2812      * version allocates 'stream' handles. I did it this way
2813      * as it is much easier and no applications should depend
2814      * on this behaviour. (Native version appears to allocate
2815      * indices into a table)
2816      */
2817     STREAM_HANDLE * pStream;
2818     HANDLE hFile;
2819
2820     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2821            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2822
2823     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2824         lpCacheEntryInfo,
2825         lpdwCacheEntryInfoBufferSize,
2826         dwReserved))
2827     {
2828         return NULL;
2829     }
2830
2831     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2832         GENERIC_READ,
2833         FILE_SHARE_READ,
2834         NULL,
2835         OPEN_EXISTING,
2836         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2837         NULL);
2838     if (hFile == INVALID_HANDLE_VALUE)
2839         return FALSE;
2840     
2841     /* allocate handle storage space */
2842     pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2843     if (!pStream)
2844     {
2845         CloseHandle(hFile);
2846         SetLastError(ERROR_OUTOFMEMORY);
2847         return FALSE;
2848     }
2849
2850     pStream->hFile = hFile;
2851     strcpy(pStream->lpszUrl, lpszUrlName);
2852     return pStream;
2853 }
2854
2855 /***********************************************************************
2856  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2857  *
2858  */
2859 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2860     IN LPCWSTR lpszUrlName,
2861     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2862     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2863     IN BOOL fRandomRead,
2864     IN DWORD dwReserved
2865     )
2866 {
2867     FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2868            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2869     return NULL;
2870 }
2871
2872 /***********************************************************************
2873  *           UnlockUrlCacheEntryStream (WININET.@)
2874  *
2875  */
2876 BOOL WINAPI UnlockUrlCacheEntryStream(
2877     IN HANDLE hUrlCacheStream,
2878     IN DWORD dwReserved
2879 )
2880 {
2881     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2882
2883     if (dwReserved != 0)
2884     {
2885         ERR("dwReserved != 0\n");
2886         SetLastError(ERROR_INVALID_PARAMETER);
2887         return FALSE;
2888     }
2889
2890     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2891     {
2892         SetLastError(ERROR_INVALID_HANDLE);
2893         return FALSE;
2894     }
2895
2896     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2897         return FALSE;
2898
2899     /* close file handle */
2900     CloseHandle(pStream->hFile);
2901
2902     /* free allocated space */
2903     HeapFree(GetProcessHeap(), 0, pStream);
2904
2905     return TRUE;
2906 }
2907
2908
2909 /***********************************************************************
2910  *           DeleteUrlCacheEntryA (WININET.@)
2911  *
2912  */
2913 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2914 {
2915     URLCACHECONTAINER * pContainer;
2916     LPURLCACHE_HEADER pHeader;
2917     struct _HASH_ENTRY * pHashEntry;
2918     CACHEFILE_ENTRY * pEntry;
2919     DWORD error;
2920
2921     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2922
2923     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2924     if (error != ERROR_SUCCESS)
2925     {
2926         SetLastError(error);
2927         return FALSE;
2928     }
2929
2930     error = URLCacheContainer_OpenIndex(pContainer);
2931     if (error != ERROR_SUCCESS)
2932     {
2933         SetLastError(error);
2934         return FALSE;
2935     }
2936
2937     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2938         return FALSE;
2939
2940     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2941     {
2942         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2943         TRACE("entry %s not found!\n", lpszUrlName);
2944         SetLastError(ERROR_FILE_NOT_FOUND);
2945         return FALSE;
2946     }
2947
2948     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2949     URLCache_DeleteEntry(pHeader, pEntry);
2950
2951     URLCache_DeleteEntryFromHash(pHashEntry);
2952
2953     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2954
2955     return TRUE;
2956 }
2957
2958 /***********************************************************************
2959  *           DeleteUrlCacheEntryW (WININET.@)
2960  *
2961  */
2962 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2963 {
2964     URLCACHECONTAINER * pContainer;
2965     LPURLCACHE_HEADER pHeader;
2966     struct _HASH_ENTRY * pHashEntry;
2967     CACHEFILE_ENTRY * pEntry;
2968     LPSTR urlA;
2969     DWORD error;
2970
2971     TRACE("(%s)\n", debugstr_w(lpszUrlName));
2972
2973     urlA = heap_strdupWtoA(lpszUrlName);
2974     if (!urlA)
2975     {
2976         SetLastError(ERROR_OUTOFMEMORY);
2977         return FALSE;
2978     }
2979
2980     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2981     if (error != ERROR_SUCCESS)
2982     {
2983         HeapFree(GetProcessHeap(), 0, urlA);
2984         SetLastError(error);
2985         return FALSE;
2986     }
2987
2988     error = URLCacheContainer_OpenIndex(pContainer);
2989     if (error != ERROR_SUCCESS)
2990     {
2991         HeapFree(GetProcessHeap(), 0, urlA);
2992         SetLastError(error);
2993         return FALSE;
2994     }
2995
2996     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2997     {
2998         HeapFree(GetProcessHeap(), 0, urlA);
2999         return FALSE;
3000     }
3001
3002     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3003     {
3004         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3005         TRACE("entry %s not found!\n", debugstr_a(urlA));
3006         HeapFree(GetProcessHeap(), 0, urlA);
3007         SetLastError(ERROR_FILE_NOT_FOUND);
3008         return FALSE;
3009     }
3010
3011     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3012     URLCache_DeleteEntry(pHeader, pEntry);
3013
3014     URLCache_DeleteEntryFromHash(pHashEntry);
3015
3016     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3017
3018     HeapFree(GetProcessHeap(), 0, urlA);
3019     return TRUE;
3020 }
3021
3022 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3023 {
3024     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3025     return TRUE;
3026 }
3027
3028 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3029 {
3030     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3031     return TRUE;
3032 }
3033
3034 /***********************************************************************
3035  *           CreateCacheContainerA (WININET.@)
3036  */
3037 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3038                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3039 {
3040     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3041           d1, d2, d3, d4, d5, d6, d7, d8);
3042     return TRUE;
3043 }
3044
3045 /***********************************************************************
3046  *           CreateCacheContainerW (WININET.@)
3047  */
3048 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3049                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3050 {
3051     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3052           d1, d2, d3, d4, d5, d6, d7, d8);
3053     return TRUE;
3054 }
3055
3056 /***********************************************************************
3057  *           FindFirstUrlCacheContainerA (WININET.@)
3058  */
3059 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3060 {
3061     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3062     return NULL;
3063 }
3064
3065 /***********************************************************************
3066  *           FindFirstUrlCacheContainerW (WININET.@)
3067  */
3068 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3069 {
3070     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3071     return NULL;
3072 }
3073
3074 /***********************************************************************
3075  *           FindNextUrlCacheContainerA (WININET.@)
3076  */
3077 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3078 {
3079     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3080     return FALSE;
3081 }
3082
3083 /***********************************************************************
3084  *           FindNextUrlCacheContainerW (WININET.@)
3085  */
3086 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3087 {
3088     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3089     return FALSE;
3090 }
3091
3092 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3093   LPCSTR lpszUrlSearchPattern,
3094   DWORD dwFlags,
3095   DWORD dwFilter,
3096   GROUPID GroupId,
3097   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3098   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3099   LPVOID lpReserved,
3100   LPDWORD pcbReserved2,
3101   LPVOID lpReserved3
3102 )
3103 {
3104     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3105           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3106           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3107     SetLastError(ERROR_FILE_NOT_FOUND);
3108     return NULL;
3109 }
3110
3111 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3112   LPCWSTR lpszUrlSearchPattern,
3113   DWORD dwFlags,
3114   DWORD dwFilter,
3115   GROUPID GroupId,
3116   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3117   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3118   LPVOID lpReserved,
3119   LPDWORD pcbReserved2,
3120   LPVOID lpReserved3
3121 )
3122 {
3123     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3124           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3125           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3126     SetLastError(ERROR_FILE_NOT_FOUND);
3127     return NULL;
3128 }
3129
3130 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3131
3132 typedef struct URLCacheFindEntryHandle
3133 {
3134     DWORD dwMagic;
3135     LPWSTR lpszUrlSearchPattern;
3136     DWORD dwContainerIndex;
3137     DWORD dwHashTableIndex;
3138     DWORD dwHashEntryIndex;
3139 } URLCacheFindEntryHandle;
3140
3141 /***********************************************************************
3142  *           FindFirstUrlCacheEntryA (WININET.@)
3143  *
3144  */
3145 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3146  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3147 {
3148     URLCacheFindEntryHandle *pEntryHandle;
3149
3150     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3151
3152     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3153     if (!pEntryHandle)
3154         return NULL;
3155
3156     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3157     if (lpszUrlSearchPattern)
3158     {
3159         pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3160         if (!pEntryHandle->lpszUrlSearchPattern)
3161         {
3162             HeapFree(GetProcessHeap(), 0, pEntryHandle);
3163             return NULL;
3164         }
3165     }
3166     else
3167         pEntryHandle->lpszUrlSearchPattern = NULL;
3168     pEntryHandle->dwContainerIndex = 0;
3169     pEntryHandle->dwHashTableIndex = 0;
3170     pEntryHandle->dwHashEntryIndex = 0;
3171
3172     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3173     {
3174         HeapFree(GetProcessHeap(), 0, pEntryHandle);
3175         return NULL;
3176     }
3177     return pEntryHandle;
3178 }
3179
3180 /***********************************************************************
3181  *           FindFirstUrlCacheEntryW (WININET.@)
3182  *
3183  */
3184 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3185  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3186 {
3187     URLCacheFindEntryHandle *pEntryHandle;
3188
3189     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3190
3191     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3192     if (!pEntryHandle)
3193         return NULL;
3194
3195     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3196     if (lpszUrlSearchPattern)
3197     {
3198         pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3199         if (!pEntryHandle->lpszUrlSearchPattern)
3200         {
3201             HeapFree(GetProcessHeap(), 0, pEntryHandle);
3202             return NULL;
3203         }
3204     }
3205     else
3206         pEntryHandle->lpszUrlSearchPattern = NULL;
3207     pEntryHandle->dwContainerIndex = 0;
3208     pEntryHandle->dwHashTableIndex = 0;
3209     pEntryHandle->dwHashEntryIndex = 0;
3210
3211     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3212     {
3213         HeapFree(GetProcessHeap(), 0, pEntryHandle);
3214         return NULL;
3215     }
3216     return pEntryHandle;
3217 }
3218
3219 /***********************************************************************
3220  *           FindNextUrlCacheEntryA (WININET.@)
3221  */
3222 BOOL WINAPI FindNextUrlCacheEntryA(
3223   HANDLE hEnumHandle,
3224   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3225   LPDWORD lpdwNextCacheEntryInfoBufferSize)
3226 {
3227     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3228     URLCACHECONTAINER * pContainer;
3229
3230     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3231
3232     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3233     {
3234         SetLastError(ERROR_INVALID_HANDLE);
3235         return FALSE;
3236     }
3237
3238     for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3239          pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3240     {
3241         LPURLCACHE_HEADER pHeader;
3242         HASH_CACHEFILE_ENTRY *pHashTableEntry;
3243         DWORD error;
3244
3245         error = URLCacheContainer_OpenIndex(pContainer);
3246         if (error != ERROR_SUCCESS)
3247         {
3248             SetLastError(error);
3249             return FALSE;
3250         }
3251
3252         if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3253             return FALSE;
3254
3255         for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3256              pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3257         {
3258             const struct _HASH_ENTRY *pHashEntry = NULL;
3259             for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3260                  pEntryHandle->dwHashEntryIndex++)
3261             {
3262                 const URL_CACHEFILE_ENTRY *pUrlEntry;
3263                 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3264
3265                 if (pEntry->dwSignature != URL_SIGNATURE)
3266                     continue;
3267
3268                 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3269                 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3270                 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3271
3272                 error = URLCache_CopyEntry(
3273                     pContainer,
3274                     pHeader,
3275                     lpNextCacheEntryInfo,
3276                     lpdwNextCacheEntryInfoBufferSize,
3277                     pUrlEntry,
3278                     FALSE /* not UNICODE */);
3279                 if (error != ERROR_SUCCESS)
3280                 {
3281                     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3282                     SetLastError(error);
3283                     return FALSE;
3284                 }
3285                 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3286
3287                 /* increment the current index so that next time the function
3288                  * is called the next entry is returned */
3289                 pEntryHandle->dwHashEntryIndex++;
3290                 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3291                 return TRUE;
3292             }
3293         }
3294
3295         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3296     }
3297
3298     SetLastError(ERROR_NO_MORE_ITEMS);
3299     return FALSE;
3300 }
3301
3302 /***********************************************************************
3303  *           FindNextUrlCacheEntryW (WININET.@)
3304  */
3305 BOOL WINAPI FindNextUrlCacheEntryW(
3306   HANDLE hEnumHandle,
3307   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3308   LPDWORD lpdwNextCacheEntryInfoBufferSize
3309 )
3310 {
3311     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3312     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3313     return FALSE;
3314 }
3315
3316 /***********************************************************************
3317  *           FindCloseUrlCache (WININET.@)
3318  */
3319 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3320 {
3321     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3322
3323     TRACE("(%p)\n", hEnumHandle);
3324
3325     if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3326     {
3327         SetLastError(ERROR_INVALID_HANDLE);
3328         return FALSE;
3329     }
3330
3331     pEntryHandle->dwMagic = 0;
3332     HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3333     HeapFree(GetProcessHeap(), 0, pEntryHandle);
3334
3335     return TRUE;
3336 }
3337
3338 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3339                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3340 {
3341     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3342           dwSearchCondition, lpGroupId, lpReserved);
3343     return NULL;
3344 }
3345
3346 BOOL WINAPI FindNextUrlCacheEntryExA(
3347   HANDLE hEnumHandle,
3348   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3349   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3350   LPVOID lpReserved,
3351   LPDWORD pcbReserved2,
3352   LPVOID lpReserved3
3353 )
3354 {
3355     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3356           lpReserved, pcbReserved2, lpReserved3);
3357     return FALSE;
3358 }
3359
3360 BOOL WINAPI FindNextUrlCacheEntryExW(
3361   HANDLE hEnumHandle,
3362   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3363   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3364   LPVOID lpReserved,
3365   LPDWORD pcbReserved2,
3366   LPVOID lpReserved3
3367 )
3368 {
3369     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3370           lpReserved, pcbReserved2, lpReserved3);
3371     return FALSE;
3372 }
3373
3374 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3375 {
3376     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3377     return FALSE;
3378 }
3379
3380 /***********************************************************************
3381  *           CreateUrlCacheGroup (WININET.@)
3382  *
3383  */
3384 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3385 {
3386   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3387   return FALSE;
3388 }
3389
3390 /***********************************************************************
3391  *           DeleteUrlCacheGroup (WININET.@)
3392  *
3393  */
3394 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3395 {
3396     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3397           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3398     return FALSE;
3399 }
3400
3401 /***********************************************************************
3402  *           SetUrlCacheEntryGroupA (WININET.@)
3403  *
3404  */
3405 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3406   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3407   LPVOID lpReserved)
3408 {
3409     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3410           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3411           pbGroupAttributes, cbGroupAttributes, lpReserved);
3412     SetLastError(ERROR_FILE_NOT_FOUND);
3413     return FALSE;
3414 }
3415
3416 /***********************************************************************
3417  *           SetUrlCacheEntryGroupW (WININET.@)
3418  *
3419  */
3420 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3421   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3422   LPVOID lpReserved)
3423 {
3424     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3425           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3426           pbGroupAttributes, cbGroupAttributes, lpReserved);
3427     SetLastError(ERROR_FILE_NOT_FOUND);
3428     return FALSE;
3429 }
3430
3431 /***********************************************************************
3432  *           GetUrlCacheConfigInfoW (WININET.@)
3433  */
3434 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3435 {
3436     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3437     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3438     return FALSE;
3439 }
3440
3441 /***********************************************************************
3442  *           GetUrlCacheConfigInfoA (WININET.@)
3443  *
3444  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3445  */
3446 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3447 {
3448     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3449     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3450     return FALSE;
3451 }
3452
3453 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3454                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3455                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3456 {
3457     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3458           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3459           lpdwGroupInfo, lpReserved);
3460     return FALSE;
3461 }
3462
3463 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3464                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3465                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3466 {
3467     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3468           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3469           lpdwGroupInfo, lpReserved);
3470     return FALSE;
3471 }
3472
3473 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3474                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3475 {
3476     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3477           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3478     return TRUE;
3479 }
3480
3481 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3482                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3483 {
3484     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3485           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3486     return TRUE;
3487 }
3488
3489 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3490 {
3491     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3492     return TRUE;
3493 }
3494
3495 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3496 {
3497     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3498     return TRUE;
3499 }
3500
3501 /***********************************************************************
3502  *           DeleteIE3Cache (WININET.@)
3503  *
3504  * Deletes the files used by the IE3 URL caching system.
3505  *
3506  * PARAMS
3507  *   hWnd        [I] A dummy window.
3508  *   hInst       [I] Instance of process calling the function.
3509  *   lpszCmdLine [I] Options used by function.
3510  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
3511  */
3512 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3513 {
3514     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3515     return 0;
3516 }
3517
3518 /***********************************************************************
3519  *           IsUrlCacheEntryExpiredA (WININET.@)
3520  *
3521  * PARAMS
3522  *   url             [I] Url
3523  *   dwFlags         [I] Unknown
3524  *   pftLastModified [O] Last modified time
3525  */
3526 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3527 {
3528     LPURLCACHE_HEADER pHeader;
3529     struct _HASH_ENTRY * pHashEntry;
3530     const CACHEFILE_ENTRY * pEntry;
3531     const URL_CACHEFILE_ENTRY * pUrlEntry;
3532     URLCACHECONTAINER * pContainer;
3533     DWORD error;
3534
3535     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3536
3537     error = URLCacheContainers_FindContainerA(url, &pContainer);
3538     if (error != ERROR_SUCCESS)
3539     {
3540         SetLastError(error);
3541         return FALSE;
3542     }
3543
3544     error = URLCacheContainer_OpenIndex(pContainer);
3545     if (error != ERROR_SUCCESS)
3546     {
3547         SetLastError(error);
3548         return FALSE;
3549     }
3550
3551     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3552         return FALSE;
3553
3554     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3555     {
3556         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3557         TRACE("entry %s not found!\n", url);
3558         SetLastError(ERROR_FILE_NOT_FOUND);
3559         return FALSE;
3560     }
3561
3562     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3563     if (pEntry->dwSignature != URL_SIGNATURE)
3564     {
3565         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3566         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3567         SetLastError(ERROR_FILE_NOT_FOUND);
3568         return FALSE;
3569     }
3570
3571     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3572
3573     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3574
3575     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3576
3577     return TRUE;
3578 }
3579
3580 /***********************************************************************
3581  *           IsUrlCacheEntryExpiredW (WININET.@)
3582  *
3583  * PARAMS
3584  *   url             [I] Url
3585  *   dwFlags         [I] Unknown
3586  *   pftLastModified [O] Last modified time
3587  */
3588 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3589 {
3590     LPURLCACHE_HEADER pHeader;
3591     struct _HASH_ENTRY * pHashEntry;
3592     const CACHEFILE_ENTRY * pEntry;
3593     const URL_CACHEFILE_ENTRY * pUrlEntry;
3594     URLCACHECONTAINER * pContainer;
3595     DWORD error;
3596
3597     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3598
3599     error = URLCacheContainers_FindContainerW(url, &pContainer);
3600     if (error != ERROR_SUCCESS)
3601     {
3602         SetLastError(error);
3603         return FALSE;
3604     }
3605
3606     error = URLCacheContainer_OpenIndex(pContainer);
3607     if (error != ERROR_SUCCESS)
3608     {
3609         SetLastError(error);
3610         return FALSE;
3611     }
3612
3613     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3614         return FALSE;
3615
3616     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3617     {
3618         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3619         TRACE("entry %s not found!\n", debugstr_w(url));
3620         SetLastError(ERROR_FILE_NOT_FOUND);
3621         return FALSE;
3622     }
3623
3624     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3625     if (pEntry->dwSignature != URL_SIGNATURE)
3626     {
3627         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3628         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3629         SetLastError(ERROR_FILE_NOT_FOUND);
3630         return FALSE;
3631     }
3632
3633     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3634
3635     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3636
3637     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3638
3639     return TRUE;
3640 }
3641
3642 /***********************************************************************
3643  *           GetDiskInfoA (WININET.@)
3644  */
3645 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3646 {
3647     BOOL ret;
3648     ULARGE_INTEGER bytes_free, bytes_total;
3649
3650     TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3651
3652     if (!path)
3653     {
3654         SetLastError(ERROR_INVALID_PARAMETER);
3655         return FALSE;
3656     }
3657
3658     if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3659     {
3660         if (cluster_size) *cluster_size = 1;
3661         if (free) *free = bytes_free.QuadPart;
3662         if (total) *total = bytes_total.QuadPart;
3663     }
3664     return ret;
3665 }
3666
3667 /***********************************************************************
3668  *           RegisterUrlCacheNotification (WININET.@)
3669  */
3670 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3671 {
3672     FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3673     return 0;
3674 }
3675
3676 /***********************************************************************
3677  *           IncrementUrlCacheHeaderData (WININET.@)
3678  */
3679 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3680 {
3681     FIXME("(%u, %p)\n", index, data);
3682     return FALSE;
3683 }