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