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