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