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