Implement CreateUrlCacheEntryW and CommitUrlCacheEntryW. Replace the
[wine] / dlls / wininet / urlcache.c
1 /*
2  * Wininet - Url Cache functions
3  *
4  * Copyright 2001,2002 CodeWeavers
5  * Copyright 2003 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #define COM_NO_WINDOWS_H
26 #include "config.h"
27 #include "wine/port.h"
28
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winuser.h"
41 #include "wininet.h"
42 #include "winerror.h"
43 #include "internet.h"
44 #include "winreg.h"
45 #include "shlwapi.h"
46 #include "wingdi.h"
47 #include "shlobj.h"
48
49 #include "wine/unicode.h"
50 #include "wine/list.h"
51 #include "wine/debug.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
54
55 #define ENTRY_START_OFFSET  0x4000
56 #define DIR_LENGTH          8
57 #define BLOCKSIZE           128
58 #define HASHTABLE_SIZE      448
59 #define HASHTABLE_BLOCKSIZE 7
60 #define HASHTABLE_FREE      3
61 #define ALLOCATION_TABLE_OFFSET 0x250
62 #define ALLOCATION_TABLE_SIZE   (0x1000 - ALLOCATION_TABLE_OFFSET)
63 #define HASHTABLE_NUM_ENTRIES   (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
64
65 #define DWORD_SIG(a,b,c,d)  (a | (b << 8) | (c << 16) | (d << 24))
66 #define URL_SIGNATURE   DWORD_SIG('U','R','L',' ')
67 #define REDR_SIGNATURE  DWORD_SIG('R','E','D','R')
68 #define LEAK_SIGNATURE  DWORD_SIG('L','E','A','K')
69 #define HASH_SIGNATURE  DWORD_SIG('H','A','S','H')
70
71 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
72
73 typedef struct _CACHEFILE_ENTRY
74 {
75 /*  union
76     {*/
77         DWORD dwSignature; /* e.g. "URL " */
78 /*      CHAR szSignature[4];
79     };*/
80     DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
81 } CACHEFILE_ENTRY;
82
83 typedef struct _URL_CACHEFILE_ENTRY
84 {
85     CACHEFILE_ENTRY CacheFileEntry;
86     FILETIME LastModifiedTime;
87     FILETIME LastAccessTime;
88     WORD wExpiredDate; /* expire date in dos format */
89     WORD wExpiredTime; /* expire time in dos format */
90     DWORD dwUnknown1; /* usually zero */
91     DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
92     DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
93     DWORD dwUnknown2; /* usually zero */
94     DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
95     DWORD dwUnknown3; /* usually 0x60 */
96     DWORD dwOffsetUrl; /* usually 0x68 */
97     BYTE CacheDir; /* index of cache directory this url is stored in */
98     BYTE Unknown4; /* usually zero */
99     WORD wUnknown5; /* usually 0x1010 */
100     DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
101     DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
102     DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
103     DWORD dwHeaderInfoSize;
104     DWORD dwUnknown6; /* usually zero */
105     WORD wLastSyncDate; /* last sync date in dos format */
106     WORD wLastSyncTime; /* last sync time in dos format */
107     DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
108     DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109     WORD wUnknownDate; /* usually same as wLastSyncDate */
110     WORD wUnknownTime; /* usually same as wLastSyncTime */
111     DWORD dwUnknown7; /* usually zero */
112     DWORD dwUnknown8; /* usually zero */
113     CHAR szSourceUrlName[1]; /* start of url */
114     /* packing to dword align start of next field */
115     /* CHAR szLocalFileName[]; (local file name exluding path) */
116     /* packing to dword align start of next field */
117     /* CHAR szHeaderInfo[]; (header info) */
118 } URL_CACHEFILE_ENTRY;
119
120 struct _HASH_ENTRY
121 {
122     DWORD dwHashKey;
123     DWORD dwOffsetEntry;
124 };
125
126 typedef struct _HASH_CACHEFILE_ENTRY
127 {
128     CACHEFILE_ENTRY CacheFileEntry;
129     DWORD dwAddressNext;
130     DWORD dwHashTableNumber;
131     struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
132 } HASH_CACHEFILE_ENTRY;
133
134 typedef struct _DIRECTORY_DATA
135 {
136     DWORD dwUnknown;
137     char filename[DIR_LENGTH];
138 } DIRECTORY_DATA;
139
140 typedef struct _URLCACHE_HEADER
141 {
142     char szSignature[28];
143     DWORD dwFileSize;
144     DWORD dwOffsetFirstHashTable;
145     DWORD dwIndexCapacityInBlocks;
146     DWORD dwBlocksInUse; /* is this right? */
147     DWORD dwUnknown1;
148     DWORD dwCacheLimitLow; /* disk space limit for cache */
149     DWORD dwCacheLimitHigh; /* disk space limit for cache */
150     DWORD dwUnknown4; /* current disk space usage for cache? */
151     DWORD dwUnknown5; /* current disk space usage for cache? */
152     DWORD dwUnknown6; /* possibly a flag? */
153     DWORD dwUnknown7;
154     BYTE DirectoryCount; /* number of directory_data's */
155     BYTE Unknown8[3]; /* just padding? */
156     DIRECTORY_DATA directory_data[1]; /* first directory entry */
157 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
158 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
159
160 typedef struct _STREAM_HANDLE
161 {
162     HANDLE hFile;
163     CHAR lpszUrl[1];
164 } STREAM_HANDLE;
165
166 typedef struct _URLCACHECONTAINER
167 {
168     struct list entry; /* part of a list */
169     LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
170     LPWSTR path; /* path to url container directory */
171     HANDLE hMapping; /* handle of file mapping */
172     DWORD file_size; /* size of file when mapping was opened */
173     HANDLE hMutex; /* hande of mutex */
174 } URLCACHECONTAINER;
175
176
177 /* List of all containers available */
178 static struct list UrlContainers = LIST_INIT(UrlContainers);
179
180
181 /***********************************************************************
182  *           URLCache_PathToObjectName (Internal)
183  *
184  *  Converts a path to a name suitable for use as a Win32 object name.
185  * Replaces '\\' characters in-place with the specified character
186  * (usually '_' or '!')
187  *
188  * RETURNS
189  *    nothing
190  *
191  */
192 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
193 {
194     for (; *lpszPath; lpszPath++)
195     {
196         if (*lpszPath == '\\')
197             *lpszPath = replace;
198     }
199 }
200
201 /***********************************************************************
202  *           URLCacheContainer_OpenIndex (Internal)
203  *
204  *  Opens the index file and saves mapping handle in hCacheIndexMapping
205  *
206  * RETURNS
207  *    TRUE if succeeded
208  *    FALSE if failed
209  *
210  */
211 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
212 {
213     HANDLE hFile;
214     WCHAR wszFilePath[MAX_PATH];
215     DWORD dwFileSize;
216
217     static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
218     static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
219
220     if (pContainer->hMapping)
221         return TRUE;
222
223     strcpyW(wszFilePath, pContainer->path);
224     strcatW(wszFilePath, wszIndex);
225
226     hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
227     if (hFile == INVALID_HANDLE_VALUE)
228     {
229         FIXME("need to create cache index file\n");
230         return FALSE;
231     }
232
233     dwFileSize = GetFileSize(hFile, NULL);
234     if (dwFileSize == INVALID_FILE_SIZE)
235         return FALSE;
236
237     if (dwFileSize == 0)
238     {
239         FIXME("need to create cache index file\n");
240         return FALSE;
241     }
242
243     wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
244     URLCache_PathToObjectName(wszFilePath, '_');
245     pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
246     if (!pContainer->hMapping)
247         pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
248     CloseHandle(hFile);
249     if (!pContainer->hMapping)
250     {
251         ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
252         return FALSE;
253     }
254
255     return TRUE;
256 }
257
258 /***********************************************************************
259  *           URLCacheContainer_CloseIndex (Internal)
260  *
261  *  Closes the index
262  *
263  * RETURNS
264  *    nothing
265  *
266  */
267 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
268 {
269     CloseHandle(pContainer->hMapping);
270     pContainer->hMapping = NULL;
271 }
272
273 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
274 {
275     URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
276     int path_len = strlenW(path);
277     int cache_prefix_len = strlenW(cache_prefix);
278
279     if (!pContainer)
280     {
281         return FALSE;
282     }
283
284     pContainer->hMapping = NULL;
285     pContainer->file_size = 0;
286
287     pContainer->path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
288     if (!pContainer->path)
289     {
290         HeapFree(GetProcessHeap(), 0, pContainer);
291         return FALSE;
292     }
293
294     memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
295
296     pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
297     if (!pContainer->cache_prefix)
298     {
299         HeapFree(GetProcessHeap(), 0, pContainer->path);
300         HeapFree(GetProcessHeap(), 0, pContainer);
301         return FALSE;
302     }
303
304     memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
305
306     CharLowerW(mutex_name);
307     URLCache_PathToObjectName(mutex_name, '!');
308
309     if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
310     {
311         ERR("couldn't create mutex (error is %ld)\n", GetLastError());
312         HeapFree(GetProcessHeap(), 0, pContainer->path);
313         HeapFree(GetProcessHeap(), 0, pContainer);
314         return FALSE;
315     }
316
317     list_add_head(&UrlContainers, &pContainer->entry);
318
319     return TRUE;
320 }
321
322 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
323 {
324     list_remove(&pContainer->entry);
325
326     URLCacheContainer_CloseIndex(pContainer);
327     CloseHandle(pContainer->hMutex);
328     HeapFree(GetProcessHeap(), 0, pContainer->path);
329     HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
330     HeapFree(GetProcessHeap(), 0, pContainer);
331 }
332
333 void URLCacheContainers_CreateDefaults()
334 {
335     static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
336     static const WCHAR UrlPrefix[] = {0};
337     static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
338     static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
339     static const WCHAR CookieSuffix[] = {0};
340     static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
341     static const struct
342     {
343         int nFolder; /* CSIDL_* constant */
344         const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
345         const WCHAR * cache_prefix; /* prefix used to reference the container */
346     } DefaultContainerData[] = 
347     {
348         { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
349         { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
350         { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
351     };
352     DWORD i;
353
354     for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
355     {
356         WCHAR wszCachePath[MAX_PATH];
357         WCHAR wszMutexName[MAX_PATH];
358         int path_len, suffix_len;
359
360         if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
361         {
362             ERR("Couldn't get path for default container %lu\n", i);
363             continue;
364         }
365         path_len = strlenW(wszCachePath);
366         suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
367
368         if (path_len + suffix_len + 2 > MAX_PATH)
369         {
370             ERR("Path too long\n");
371             continue;
372         }
373
374         wszCachePath[path_len] = '\\';
375
376         strcpyW(wszMutexName, wszCachePath);
377         
378         if (suffix_len)
379         {
380             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
381             wszCachePath[path_len + suffix_len + 1] = '\\';
382             wszCachePath[path_len + suffix_len + 2] = '\0';
383         }
384
385         URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
386     }
387 }
388
389 void URLCacheContainers_DeleteAll()
390 {
391     while(!list_empty(&UrlContainers))
392         URLCacheContainer_DeleteContainer(
393             LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
394         );
395 }
396
397 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
398 {
399     struct list * cursor;
400
401     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
402
403     LIST_FOR_EACH(cursor, &UrlContainers)
404     {
405         URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
406         int prefix_len = strlenW(pContainer->cache_prefix);
407         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
408         {
409             TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
410             *ppContainer = pContainer;
411             return TRUE;
412         }
413     }
414     ERR("no container found\n");
415     SetLastError(ERROR_FILE_NOT_FOUND);
416     return FALSE;
417 }
418
419 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
420 {
421     BOOL ret;
422     LPWSTR lpwszUrl;
423     int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
424     if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
425     {
426         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
427         ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
428         HeapFree(GetProcessHeap(), 0, lpwszUrl);
429         return ret;
430     }
431     return FALSE;
432 }
433
434 /***********************************************************************
435  *           URLCacheContainer_LockIndex (Internal)
436  *
437  */
438 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
439 {
440     BYTE index;
441     LPVOID pIndexData;
442     URLCACHE_HEADER * pHeader;
443
444     /* acquire mutex */
445     WaitForSingleObject(pContainer->hMutex, INFINITE);
446
447     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
448
449     if (!pIndexData)
450     {
451         ReleaseMutex(pContainer->hMutex);
452         ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
453         return FALSE;
454     }
455     pHeader = (URLCACHE_HEADER *)pIndexData;
456
457     /* file has grown - we need to remap to prevent us getting
458      * access violations when we try and access beyond the end
459      * of the memory mapped file */
460     if (pHeader->dwFileSize != pContainer->file_size)
461     {
462         URLCacheContainer_CloseIndex(pContainer);
463         if (!URLCacheContainer_OpenIndex(pContainer))
464         {
465             ReleaseMutex(pContainer->hMutex);
466             return FALSE;
467         }
468         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
469
470         if (!pIndexData)
471         {
472             ReleaseMutex(pContainer->hMutex);
473             ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
474             return FALSE;
475         }
476         pHeader = (URLCACHE_HEADER *)pIndexData;
477     }
478
479     TRACE("Signature: %s, file size: %ld bytes\n", pHeader->szSignature, pHeader->dwFileSize);
480
481     for (index = 0; index < pHeader->DirectoryCount; index++)
482     {
483         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
484     }
485     
486     return pHeader;
487 }
488
489 /***********************************************************************
490  *           URLCacheContainer_UnlockIndex (Internal)
491  *
492  */
493 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
494 {
495     /* release mutex */
496     ReleaseMutex(pContainer->hMutex);
497     return UnmapViewOfFile(pHeader);
498 }
499
500
501 #ifndef CHAR_BIT
502 #define CHAR_BIT    (8 * sizeof(CHAR))
503 #endif
504
505 /***********************************************************************
506  *           URLCache_Allocation_BlockIsFree (Internal)
507  *
508  *  Is the specified block number free?
509  *
510  * RETURNS
511  *    zero if free
512  *    non-zero otherwise
513  *
514  */
515 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
516 {
517     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
518     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
519 }
520
521 /***********************************************************************
522  *           URLCache_Allocation_BlockFree (Internal)
523  *
524  *  Marks the specified block as free
525  *
526  * RETURNS
527  *    nothing
528  *
529  */
530 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
531 {
532     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
533     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
534 }
535
536 /***********************************************************************
537  *           URLCache_Allocation_BlockAllocate (Internal)
538  *
539  *  Marks the specified block as allocated
540  *
541  * RETURNS
542  *    nothing
543  *
544  */
545 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
546 {
547     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
548     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
549 }
550
551 /***********************************************************************
552  *           URLCache_FindFirstFreeEntry (Internal)
553  *
554  *  Finds and allocates the first block of free space big enough and
555  * sets ppEntry to point to it.
556  *
557  * RETURNS
558  *    TRUE if it had enough space
559  *    FALSE if it couldn't find enough space
560  *
561  */
562 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
563 {
564     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
565     DWORD dwBlockNumber;
566     DWORD dwFreeCounter;
567     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
568     {
569         for (dwFreeCounter = 0; 
570             dwFreeCounter < dwBlocksNeeded &&
571               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
572               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
573             dwFreeCounter++)
574                 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
575
576         if (dwFreeCounter == dwBlocksNeeded)
577         {
578             DWORD index;
579             TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
580             for (index = 0; index < dwBlocksNeeded; index++)
581                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
582             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
583             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
584             return TRUE;
585         }
586     }
587     FIXME("Grow file\n");
588     return FALSE;
589 }
590
591 /***********************************************************************
592  *           URLCache_DeleteEntry (Internal)
593  *
594  *  Deletes the specified entry and frees the space allocated to it
595  *
596  * RETURNS
597  *    TRUE if it succeeded
598  *    FALSE if it failed
599  *
600  */
601 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
602 {
603     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
604     return TRUE;
605 }
606
607 /***********************************************************************
608  *           URLCache_LocalFileNameToPathW (Internal)
609  *
610  *  Copies the full path to the specified buffer given the local file
611  * name and the index of the directory it is in. Always sets value in
612  * lpBufferSize to the required buffer size (in bytes).
613  *
614  * RETURNS
615  *    TRUE if the buffer was big enough
616  *    FALSE if the buffer was too small
617  *
618  */
619 static BOOL URLCache_LocalFileNameToPathW(
620     const URLCACHECONTAINER * pContainer,
621     LPCURLCACHE_HEADER pHeader,
622     LPCSTR szLocalFileName,
623     BYTE Directory,
624     LPWSTR wszPath,
625     LPLONG lpBufferSize)
626 {
627     LONG nRequired;
628     int path_len = strlenW(pContainer->path);
629     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
630     if (Directory >= pHeader->DirectoryCount)
631     {
632         *lpBufferSize = 0;
633         return FALSE;
634     }
635
636     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
637     if (nRequired < *lpBufferSize)
638     {
639         int dir_len;
640
641         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
642         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
643         wszPath[dir_len + path_len] = '\\';
644         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
645         *lpBufferSize = nRequired;
646         return TRUE;
647     }
648     *lpBufferSize = nRequired;
649     return FALSE;
650 }
651
652 /***********************************************************************
653  *           URLCache_LocalFileNameToPathA (Internal)
654  *
655  *  Copies the full path to the specified buffer given the local file
656  * name and the index of the directory it is in. Always sets value in
657  * lpBufferSize to the required buffer size.
658  *
659  * RETURNS
660  *    TRUE if the buffer was big enough
661  *    FALSE if the buffer was too small
662  *
663  */
664 static BOOL URLCache_LocalFileNameToPathA(
665     const URLCACHECONTAINER * pContainer,
666     LPCURLCACHE_HEADER pHeader,
667     LPCSTR szLocalFileName,
668     BYTE Directory,
669     LPSTR szPath,
670     LPLONG lpBufferSize)
671 {
672     LONG nRequired;
673     int path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
674     int file_name_len = strlen(szLocalFileName);
675     int dir_len = DIR_LENGTH;
676     if (Directory >= pHeader->DirectoryCount)
677     {
678         *lpBufferSize = 0;
679         return FALSE;
680     }
681
682     nRequired = (path_len + dir_len + file_name_len + 1) * sizeof(WCHAR);
683     if (nRequired < *lpBufferSize)
684     {
685         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
686         strncpy(szPath, pHeader->directory_data[Directory].filename, DIR_LENGTH);
687         szPath[dir_len + path_len] = '\\';
688         strcpy(szPath + dir_len + path_len + 1, szLocalFileName);
689         *lpBufferSize = nRequired;
690         return TRUE;
691     }
692     *lpBufferSize = nRequired;
693     return FALSE;
694 }
695
696 /***********************************************************************
697  *           URLCache_CopyEntry (Internal)
698  *
699  *  Copies an entry from the cache index file to the Win32 structure
700  *
701  * RETURNS
702  *    TRUE if the buffer was big enough
703  *    FALSE if the buffer was too small
704  *
705  */
706 static BOOL URLCache_CopyEntry(
707     URLCACHECONTAINER * pContainer,
708     LPCURLCACHE_HEADER pHeader, 
709     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
710     LPDWORD lpdwBufferSize, 
711     URL_CACHEFILE_ENTRY * pUrlEntry,
712     BOOL bUnicode)
713 {
714     int lenUrl;
715     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
716
717     if (*lpdwBufferSize >= dwRequiredSize)
718     {
719         lpCacheEntryInfo->lpHeaderInfo = NULL;
720         lpCacheEntryInfo->lpszFileExtension = NULL;
721         lpCacheEntryInfo->lpszLocalFileName = NULL;
722         lpCacheEntryInfo->lpszSourceUrlName = NULL;
723         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
724         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
725         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
726         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
727         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
728         lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
729         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
730         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
731         DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
732         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
733         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
734         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
735         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
736         DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
737     }
738
739     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
740         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
741     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
742     if (bUnicode)
743         lenUrl = MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, NULL, 0);
744     else
745         lenUrl = strlen(pUrlEntry->szSourceUrlName);
746     dwRequiredSize += lenUrl + 1;
747     
748     /* FIXME: is source url optional? */
749     if (*lpdwBufferSize >= dwRequiredSize)
750     {
751         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
752         if (bUnicode)
753             MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
754         else
755             memcpy(lpCacheEntryInfo->lpszSourceUrlName, pUrlEntry->szSourceUrlName, (lenUrl + 1) * sizeof(CHAR));
756     }
757
758     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
759         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
760     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
761
762     if (pUrlEntry->dwOffsetLocalName)
763     {
764         LONG nLocalFilePathSize;
765         LPSTR lpszLocalFileName;
766         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
767         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
768         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
769             URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
770         {
771             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
772         }
773         dwRequiredSize += nLocalFilePathSize;
774
775         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
776             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
777         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
778     }
779     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
780
781     if (*lpdwBufferSize >= dwRequiredSize)
782     {
783         lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
784         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
785         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
786     }
787     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
788         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
789     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
790
791     if (dwRequiredSize > *lpdwBufferSize)
792     {
793         *lpdwBufferSize = dwRequiredSize;
794         SetLastError(ERROR_INSUFFICIENT_BUFFER);
795         return FALSE;
796     }
797     *lpdwBufferSize = dwRequiredSize;
798     return TRUE;
799 }
800
801
802 /***********************************************************************
803  *           URLCache_SetEntryInfo (Internal)
804  *
805  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
806  * according the the flags set by dwFieldControl.
807  *
808  * RETURNS
809  *    TRUE if the buffer was big enough
810  *    FALSE if the buffer was too small
811  *
812  */
813 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
814 {
815     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
816         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
817     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
818         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
819     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
820         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
821     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
822         FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
823     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
824         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
825     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
826         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
827     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
828         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
829     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
830         FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
831
832     return TRUE;
833 }
834
835 /***********************************************************************
836  *           URLCache_HashKey (Internal)
837  *
838  *  Returns the hash key for a given string
839  *
840  * RETURNS
841  *    hash key for the string
842  *
843  */
844 static DWORD URLCache_HashKey(LPCSTR lpszKey)
845 {
846     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
847      * but the algorithm and result are not the same!
848      */
849     static const unsigned char lookupTable[256] = 
850     {
851         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
852         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
853         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
854         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
855         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
856         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
857         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
858         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
859         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
860         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
861         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
862         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
863         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
864         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
865         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
866         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
867         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
868         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
869         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
870         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
871         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
872         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
873         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
874         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
875         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
876         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
877         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
878         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
879         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
880         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
881         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
882         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
883     };
884     BYTE key[4];
885     DWORD i;
886     int subscript[sizeof(key) / sizeof(key[0])];
887
888     subscript[0] = *lpszKey;
889     subscript[1] = (char)(*lpszKey + 1);
890     subscript[2] = (char)(*lpszKey + 2);
891     subscript[3] = (char)(*lpszKey + 3);
892
893     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
894         key[i] = lookupTable[i];
895
896     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
897     {
898         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
899             key[i] = lookupTable[*lpszKey ^ key[i]];
900     }
901
902     return *(DWORD *)key;
903 }
904
905 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
906 {
907     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
908 }
909
910 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
911 {
912     /* structure of hash table:
913      *  448 entries divided into 64 blocks
914      *  each block therefore contains a chain of 7 key/offset pairs
915      * how position in table is calculated:
916      *  1. the url is hashed in helper function
917      *  2. the key % 64 * 8 is the offset
918      *  3. the key in the hash table is the hash key aligned to 64
919      *
920      * note:
921      *  there can be multiple hash tables in the file and the offset to
922      *  the next one is stored in the header of the hash table
923      */
924     DWORD key = URLCache_HashKey(lpszUrl);
925     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
926     HASH_CACHEFILE_ENTRY * pHashEntry;
927     DWORD dwHashTableNumber = 0;
928
929     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
930
931     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
932          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
933          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
934     {
935         int i;
936         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
937         {
938             ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
939             continue;
940         }
941         /* make sure that it is in fact a hash entry */
942         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
943         {
944             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
945             continue;
946         }
947
948         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
949         {
950             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
951             if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
952             {
953                 /* FIXME: we should make sure that this is the right element
954                  * before returning and claiming that it is. We can do this
955                  * by doing a simple compare between the URL we were given
956                  * and the URL stored in the entry. However, this assumes
957                  * we know the format of all the entries stored in the
958                  * hash table */
959                 *ppHashEntry = pHashElement;
960                 return TRUE;
961             }
962         }
963     }
964     return FALSE;
965 }
966
967 /***********************************************************************
968  *           URLCache_FindEntryInHash (Internal)
969  *
970  *  Searches all the hash tables in the index for the given URL and
971  * returns the entry, if it was found, in ppEntry
972  *
973  * RETURNS
974  *    TRUE if the entry was found
975  *    FALSE if the entry could not be found
976  *
977  */
978 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
979 {
980     struct _HASH_ENTRY * pHashEntry;
981     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
982     {
983         *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
984         return TRUE;
985     }
986     return FALSE;
987 }
988
989 /***********************************************************************
990  *           URLCache_HashEntrySetUse (Internal)
991  *
992  *  Searches all the hash tables in the index for the given URL and
993  * sets the use count (stored or'ed with key)
994  *
995  * RETURNS
996  *    TRUE if the entry was found
997  *    FALSE if the entry could not be found
998  *
999  */
1000 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1001 {
1002     struct _HASH_ENTRY * pHashEntry;
1003     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1004     {
1005         pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1006         return TRUE;
1007     }
1008     return FALSE;
1009 }
1010
1011 /***********************************************************************
1012  *           URLCache_DeleteEntryFromHash (Internal)
1013  *
1014  *  Searches all the hash tables in the index for the given URL and
1015  * then if found deletes the entry.
1016  *
1017  * RETURNS
1018  *    TRUE if the entry was found
1019  *    FALSE if the entry could not be found
1020  *
1021  */
1022 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1023 {
1024     struct _HASH_ENTRY * pHashEntry;
1025     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1026     {
1027         pHashEntry->dwHashKey = HASHTABLE_FREE;
1028         pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1029         return TRUE;
1030     }
1031     return FALSE;
1032 }
1033
1034 /***********************************************************************
1035  *           URLCache_AddEntryToHash (Internal)
1036  *
1037  *  Searches all the hash tables for a free slot based on the offset
1038  * generated from the hash key. If a free slot is found, the offset and
1039  * key are entered into the hash table.
1040  *
1041  * RETURNS
1042  *    TRUE if the entry was added
1043  *    FALSE if the entry could not be added
1044  *
1045  */
1046 static BOOL URLCache_AddEntryToHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1047 {
1048     /* see URLCache_FindEntryInHash for structure of hash tables */
1049
1050     DWORD key = URLCache_HashKey(lpszUrl);
1051     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1052     HASH_CACHEFILE_ENTRY * pHashEntry;
1053     DWORD dwHashTableNumber = 0;
1054
1055     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1056
1057     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1058          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1059          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1060     {
1061         int i;
1062         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1063         {
1064             ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1065             break;
1066         }
1067         /* make sure that it is in fact a hash entry */
1068         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1069         {
1070             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1071             break;
1072         }
1073
1074         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1075         {
1076             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1077             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1078             {
1079                 pHashElement->dwHashKey = key;
1080                 pHashElement->dwOffsetEntry = dwOffsetEntry;
1081                 return TRUE;
1082             }
1083         }
1084     }
1085     FIXME("need to create another hash table\n");
1086     return FALSE;
1087 }
1088
1089 /***********************************************************************
1090  *           GetUrlCacheEntryInfoExA (WININET.@)
1091  *
1092  */
1093 BOOL WINAPI GetUrlCacheEntryInfoExA(
1094     LPCSTR lpszUrl,
1095     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1096     LPDWORD lpdwCacheEntryInfoBufSize,
1097     LPSTR lpszReserved,
1098     LPDWORD lpdwReserved,
1099     LPVOID lpReserved,
1100     DWORD dwFlags)
1101 {
1102     TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1103         debugstr_a(lpszUrl), 
1104         lpCacheEntryInfo,
1105         lpdwCacheEntryInfoBufSize,
1106         lpszReserved,
1107         lpdwReserved,
1108         lpReserved,
1109         dwFlags);
1110
1111     if ((lpszReserved != NULL) ||
1112         (lpdwReserved != NULL) ||
1113         (lpReserved != NULL))
1114     {
1115         ERR("Reserved value was not 0\n");
1116         SetLastError(ERROR_INVALID_PARAMETER);
1117         return FALSE;
1118     }
1119     if (dwFlags != 0)
1120         FIXME("Undocumented flag(s): %lx\n", dwFlags);
1121     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1122 }
1123
1124 /***********************************************************************
1125  *           GetUrlCacheEntryInfoA (WININET.@)
1126  *
1127  */
1128 BOOL WINAPI GetUrlCacheEntryInfoA(
1129     IN LPCSTR lpszUrlName,
1130     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1131     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1132 )
1133 {
1134     LPURLCACHE_HEADER pHeader;
1135     CACHEFILE_ENTRY * pEntry;
1136     URL_CACHEFILE_ENTRY * pUrlEntry;
1137     URLCACHECONTAINER * pContainer;
1138
1139     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1140
1141     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1142         return FALSE;
1143
1144     if (!URLCacheContainer_OpenIndex(pContainer))
1145         return FALSE;
1146
1147     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1148         return FALSE;
1149
1150     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1151     {
1152         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1153         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1154         SetLastError(ERROR_FILE_NOT_FOUND);
1155         return FALSE;
1156     }
1157
1158     if (pEntry->dwSignature != URL_SIGNATURE)
1159     {
1160         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1161         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1162         SetLastError(ERROR_FILE_NOT_FOUND);
1163         return FALSE;
1164     }
1165
1166     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1167     TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1168     if (pUrlEntry->dwOffsetHeaderInfo)
1169         TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1170
1171     if (!URLCache_CopyEntry(
1172         pContainer,
1173         pHeader,
1174         lpCacheEntryInfo,
1175         lpdwCacheEntryInfoBufferSize,
1176         pUrlEntry,
1177         FALSE /* ANSI */))
1178     {
1179         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1180         return FALSE;
1181     }
1182     TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1183
1184     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1185
1186     return TRUE;
1187 }
1188
1189 /***********************************************************************
1190  *           GetUrlCacheEntryInfoW (WININET.@)
1191  *
1192  */
1193 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1194   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1195   LPDWORD lpdwCacheEntryInfoBufferSize)
1196 {
1197     LPURLCACHE_HEADER pHeader;
1198     CACHEFILE_ENTRY * pEntry;
1199     URL_CACHEFILE_ENTRY * pUrlEntry;
1200     URLCACHECONTAINER * pContainer;
1201     LPSTR lpszUrlA;
1202     int url_len;
1203
1204     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1205
1206     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1207     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1208     if (!lpszUrlA)
1209     {
1210         SetLastError(ERROR_OUTOFMEMORY);
1211         return FALSE;
1212     }
1213     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1214
1215     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1216     {
1217         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1218         return FALSE;
1219     }
1220
1221     if (!URLCacheContainer_OpenIndex(pContainer))
1222     {
1223         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1224         return FALSE;
1225     }
1226
1227     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1228     {
1229         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1230         return FALSE;
1231     }
1232
1233     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1234     {
1235         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1236         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1237         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1238         SetLastError(ERROR_FILE_NOT_FOUND);
1239         return FALSE;
1240     }
1241     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1242
1243     if (pEntry->dwSignature != URL_SIGNATURE)
1244     {
1245         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1246         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1247         SetLastError(ERROR_FILE_NOT_FOUND);
1248         return FALSE;
1249     }
1250
1251     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1252     TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1253     TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1254
1255     if (!URLCache_CopyEntry(
1256         pContainer,
1257         pHeader,
1258         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1259         lpdwCacheEntryInfoBufferSize,
1260         pUrlEntry,
1261         TRUE /* UNICODE */))
1262     {
1263         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1264         return FALSE;
1265     }
1266     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1267
1268     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1269
1270     return TRUE;
1271 }
1272
1273 /***********************************************************************
1274  *           GetUrlCacheEntryInfoExW (WININET.@)
1275  *
1276  */
1277 BOOL WINAPI GetUrlCacheEntryInfoExW(
1278     LPCWSTR lpszUrl,
1279     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1280     LPDWORD lpdwCacheEntryInfoBufSize,
1281     LPWSTR lpszReserved,
1282     LPDWORD lpdwReserved,
1283     LPVOID lpReserved,
1284     DWORD dwFlags)
1285 {
1286     TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1287         debugstr_w(lpszUrl), 
1288         lpCacheEntryInfo,
1289         lpdwCacheEntryInfoBufSize,
1290         lpszReserved,
1291         lpdwReserved,
1292         lpReserved,
1293         dwFlags);
1294
1295     if ((lpszReserved != NULL) ||
1296         (lpdwReserved != NULL) ||
1297         (lpReserved != NULL))
1298     {
1299         ERR("Reserved value was not 0\n");
1300         SetLastError(ERROR_INVALID_PARAMETER);
1301         return FALSE;
1302     }
1303     if (dwFlags != 0)
1304         FIXME("Undocumented flag(s): %lx\n", dwFlags);
1305     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1306 }
1307
1308 /***********************************************************************
1309  *           SetUrlCacheEntryInfoA (WININET.@)
1310  */
1311 BOOL WINAPI SetUrlCacheEntryInfoA(
1312     LPCSTR lpszUrlName,
1313     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1314     DWORD dwFieldControl)
1315 {
1316     LPURLCACHE_HEADER pHeader;
1317     CACHEFILE_ENTRY * pEntry;
1318     URLCACHECONTAINER * pContainer;
1319
1320     TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1321
1322     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1323         return FALSE;
1324
1325     if (!URLCacheContainer_OpenIndex(pContainer))
1326         return FALSE;
1327
1328     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1329         return FALSE;
1330
1331     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1332     {
1333         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1334         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1335         SetLastError(ERROR_FILE_NOT_FOUND);
1336         return FALSE;
1337     }
1338
1339     if (pEntry->dwSignature != URL_SIGNATURE)
1340     {
1341         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1342         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1343         SetLastError(ERROR_FILE_NOT_FOUND);
1344         return FALSE;
1345     }
1346
1347     URLCache_SetEntryInfo(
1348         (URL_CACHEFILE_ENTRY *)pEntry,
1349         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1350         dwFieldControl);
1351
1352     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1353
1354     return TRUE;
1355 }
1356
1357 /***********************************************************************
1358  *           SetUrlCacheEntryInfoW (WININET.@)
1359  */
1360 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1361 {
1362     LPURLCACHE_HEADER pHeader;
1363     CACHEFILE_ENTRY * pEntry;
1364     URLCACHECONTAINER * pContainer;
1365     LPSTR lpszUrlA;
1366     int url_len;
1367
1368     TRACE("(%s, %p, 0x%08lx)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1369
1370     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1371     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1372     if (!lpszUrlA)
1373     {
1374         SetLastError(ERROR_OUTOFMEMORY);
1375         return FALSE;
1376     }
1377     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1378
1379     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1380     {
1381         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1382         return FALSE;
1383     }
1384
1385     if (!URLCacheContainer_OpenIndex(pContainer))
1386     {
1387         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1388         return FALSE;
1389     }
1390
1391     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1392     {
1393         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1394         return FALSE;
1395     }
1396
1397     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1398     {
1399         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1400         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1401         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1402         SetLastError(ERROR_FILE_NOT_FOUND);
1403         return FALSE;
1404     }
1405     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1406
1407     if (pEntry->dwSignature != URL_SIGNATURE)
1408     {
1409         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1410         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1411         SetLastError(ERROR_FILE_NOT_FOUND);
1412         return FALSE;
1413     }
1414
1415     URLCache_SetEntryInfo(
1416         (URL_CACHEFILE_ENTRY *)pEntry,
1417         lpCacheEntryInfo,
1418         dwFieldControl);
1419
1420     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1421
1422     return TRUE;
1423 }
1424
1425 /***********************************************************************
1426  *           RetrieveUrlCacheEntryFileA (WININET.@)
1427  *
1428  */
1429 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1430     IN LPCSTR lpszUrlName,
1431     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1432     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1433     IN DWORD dwReserved
1434     )
1435 {
1436     LPURLCACHE_HEADER pHeader;
1437     CACHEFILE_ENTRY * pEntry;
1438     URL_CACHEFILE_ENTRY * pUrlEntry;
1439     URLCACHECONTAINER * pContainer;
1440
1441     TRACE("(%s, %p, %p, 0x%08lx)\n",
1442         debugstr_a(lpszUrlName),
1443         lpCacheEntryInfo,
1444         lpdwCacheEntryInfoBufferSize,
1445         dwReserved);
1446
1447     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1448         return FALSE;
1449
1450     if (!URLCacheContainer_OpenIndex(pContainer))
1451         return FALSE;
1452
1453     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1454         return FALSE;
1455
1456     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1457     {
1458         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1459         TRACE("entry %s not found!\n", lpszUrlName);
1460         SetLastError(ERROR_FILE_NOT_FOUND);
1461         return FALSE;
1462     }
1463
1464     if (pEntry->dwSignature != URL_SIGNATURE)
1465     {
1466         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1467         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1468         SetLastError(ERROR_FILE_NOT_FOUND);
1469         return FALSE;
1470     }
1471
1472     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1473     TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
1474     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1475
1476     pUrlEntry->dwHitRate++;
1477     pUrlEntry->dwUseCount++;
1478     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1479
1480     if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1481     {
1482         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1483         return FALSE;
1484     }
1485     TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1486
1487     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1488
1489     return TRUE;
1490 }
1491
1492 /***********************************************************************
1493  *           RetrieveUrlCacheEntryFileW (WININET.@)
1494  *
1495  */
1496 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1497     IN LPCWSTR lpszUrlName,
1498     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1499     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1500     IN DWORD dwReserved
1501     )
1502 {
1503     TRACE("(%s, %p, %p, 0x%08lx)\n",
1504         debugstr_w(lpszUrlName),
1505         lpCacheEntryInfo,
1506         lpdwCacheEntryInfoBufferSize,
1507         dwReserved);
1508
1509     return FALSE;
1510 }
1511
1512 /***********************************************************************
1513  *           UnlockUrlCacheEntryFileA (WININET.@)
1514  *
1515  */
1516 BOOL WINAPI UnlockUrlCacheEntryFileA(
1517     IN LPCSTR lpszUrlName, 
1518     IN DWORD dwReserved
1519     )
1520 {
1521     LPURLCACHE_HEADER pHeader;
1522     CACHEFILE_ENTRY * pEntry;
1523     URL_CACHEFILE_ENTRY * pUrlEntry;
1524     URLCACHECONTAINER * pContainer;
1525
1526     TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName), dwReserved);
1527
1528     if (dwReserved)
1529     {
1530         ERR("dwReserved != 0\n");
1531         SetLastError(ERROR_INVALID_PARAMETER);
1532         return FALSE;
1533     }
1534
1535     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1536        return FALSE;
1537
1538     if (!URLCacheContainer_OpenIndex(pContainer))
1539         return FALSE;
1540
1541     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1542         return FALSE;
1543
1544     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1545     {
1546         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1547         TRACE("entry %s not found!\n", lpszUrlName);
1548         SetLastError(ERROR_FILE_NOT_FOUND);
1549         return FALSE;
1550     }
1551
1552     if (pEntry->dwSignature != URL_SIGNATURE)
1553     {
1554         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1555         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1556         SetLastError(ERROR_FILE_NOT_FOUND);
1557         return FALSE;
1558     }
1559
1560     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1561
1562     if (pUrlEntry->dwUseCount == 0)
1563     {
1564         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1565         return FALSE;
1566     }
1567     pUrlEntry->dwUseCount--;
1568     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1569
1570     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1571
1572     return TRUE;
1573 }
1574
1575 /***********************************************************************
1576  *           UnlockUrlCacheEntryFileW (WININET.@)
1577  *
1578  */
1579 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1580 {
1581     FIXME("(%s, 0x%08lx)\n", debugstr_w(lpszUrlName), dwReserved);
1582     return TRUE;
1583 }
1584
1585 /***********************************************************************
1586  *           CreateUrlCacheEntryA (WININET.@)
1587  *
1588  */
1589 BOOL WINAPI CreateUrlCacheEntryA(
1590     IN LPCSTR lpszUrlName,
1591     IN DWORD dwExpectedFileSize,
1592     IN LPCSTR lpszFileExtension,
1593     OUT LPSTR lpszFileName,
1594     IN DWORD dwReserved
1595 )
1596 {
1597     DWORD len;
1598     WCHAR *url_name;
1599     WCHAR *file_extension;
1600     WCHAR file_name[MAX_PATH];
1601     BOOL bSuccess = FALSE;
1602     DWORD dwError = 0;
1603
1604     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
1605         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1606     {
1607         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
1608         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
1609             (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1610         {
1611             MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
1612             if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
1613             {
1614                 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
1615                 {
1616                     bSuccess = TRUE;
1617                 }
1618                 else
1619                 {
1620                     dwError = GetLastError();
1621                 }
1622             }
1623             else
1624             {
1625                 dwError = GetLastError();
1626             }
1627             HeapFree(GetProcessHeap(), 0, file_extension);
1628         }
1629         else
1630         {
1631             dwError = GetLastError();
1632         }
1633         HeapFree(GetProcessHeap(), 0, url_name);
1634         if (!bSuccess)
1635             SetLastError(dwError);
1636     }
1637     return bSuccess;
1638 }
1639 /***********************************************************************
1640  *           CreateUrlCacheEntryW (WININET.@)
1641  *
1642  */
1643 BOOL WINAPI CreateUrlCacheEntryW(
1644     IN LPCWSTR lpszUrlName,
1645     IN DWORD dwExpectedFileSize,
1646     IN LPCWSTR lpszFileExtension,
1647     OUT LPWSTR lpszFileName,
1648     IN DWORD dwReserved
1649 )
1650 {
1651     URLCACHECONTAINER * pContainer;
1652     LPURLCACHE_HEADER pHeader;
1653     CHAR szFile[MAX_PATH];
1654     WCHAR szExtension[MAX_PATH];
1655     LPCWSTR lpszUrlPart;
1656     LPCWSTR lpszUrlEnd;
1657     LPCWSTR lpszFileNameExtension;
1658     LPWSTR lpszFileNameNoPath;
1659     int i;
1660     int countnoextension;
1661     BYTE CacheDir;
1662     LONG lBufferSize;
1663     BOOL bFound = FALSE;
1664     int count;
1665     static WCHAR szWWW[] = {'w','w','w',0};
1666
1667     TRACE("(%s, 0x%08lx, %s, %p, 0x%08lx)\n",
1668         debugstr_w(lpszUrlName),
1669         dwExpectedFileSize,
1670         debugstr_w(lpszFileExtension),
1671         lpszFileName,
1672         dwReserved);
1673
1674     if (dwReserved)
1675     {
1676         ERR("dwReserved != 0\n");
1677         SetLastError(ERROR_INVALID_PARAMETER);
1678         return FALSE;
1679     }
1680
1681     for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1682         ;
1683     
1684     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1685         lpszUrlEnd--;
1686
1687     for (lpszUrlPart = lpszUrlEnd; 
1688         (lpszUrlPart >= lpszUrlName); 
1689         lpszUrlPart--)
1690     {
1691         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1692         {
1693             bFound = TRUE;
1694             lpszUrlPart++;
1695             break;
1696         }
1697     }
1698     if (!lstrcmpW(lpszUrlPart, szWWW))
1699     {
1700         lpszUrlPart += lstrlenW(szWWW);
1701     }
1702
1703     count = lpszUrlEnd - lpszUrlPart;
1704
1705     if (bFound && (count < MAX_PATH))
1706     {
1707         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
1708         if (!len)
1709             return FALSE;
1710         szFile[len] = '\0';
1711         /* FIXME: get rid of illegal characters like \, / and : */
1712     }
1713     else
1714     {
1715         FIXME("need to generate a random filename\n");
1716     }
1717
1718     TRACE("File name: %s\n", debugstr_a(szFile));
1719
1720     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1721         return FALSE;
1722
1723     if (!URLCacheContainer_OpenIndex(pContainer))
1724         return FALSE;
1725
1726     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1727         return FALSE;
1728
1729     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1730
1731     lBufferSize = MAX_PATH * sizeof(CHAR);
1732     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1733
1734     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1735
1736     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(CHAR) - 2; 
1737         lpszFileNameNoPath >= lpszFileName; 
1738         --lpszFileNameNoPath)
1739     {
1740         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
1741             break;
1742     }
1743
1744     countnoextension = lstrlenW(lpszFileNameNoPath);
1745     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
1746     if (lpszFileNameExtension)
1747         countnoextension -= lstrlenW(lpszFileNameExtension);
1748     *szExtension = '\0';
1749
1750     if (lpszFileExtension)
1751     {
1752         szExtension[0] = '.';
1753         lstrcpyW(szExtension+1, lpszFileExtension);
1754     }
1755
1756     for (i = 0; i < 255; i++)
1757     {
1758         static WCHAR szFormat[] = {'[','%','u',']','%','s',0};
1759         HANDLE hFile;
1760         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
1761         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
1762         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1763         if (hFile != INVALID_HANDLE_VALUE)
1764         {
1765             CloseHandle(hFile);
1766             return TRUE;
1767         }
1768     }
1769
1770     return FALSE;
1771 }
1772
1773
1774 /***********************************************************************
1775  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
1776  *
1777  *   The bug we are compensating for is that some drongo at Microsoft
1778  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
1779  *   As a consequence, CommitUrlCacheEntryA has been effectively
1780  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
1781  *   is still defined as LPCWSTR. The result (other than madness) is
1782  *   that we always need to store lpHeaderInfo in CP_ACP rather than
1783  *   in UTF16, and we need to avoid converting lpHeaderInfo in
1784  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
1785  *   result will lose data for arbitrary binary data.
1786  *
1787  */
1788 static BOOL WINAPI CommitUrlCacheEntryInternal(
1789     IN LPCWSTR lpszUrlName,
1790     IN LPCWSTR lpszLocalFileName,
1791     IN FILETIME ExpireTime,
1792     IN FILETIME LastModifiedTime,
1793     IN DWORD CacheEntryType,
1794     IN LPBYTE lpHeaderInfo,
1795     IN DWORD dwHeaderSize,
1796     IN LPCWSTR lpszFileExtension,
1797     IN LPCWSTR lpszOriginalUrl
1798     )
1799 {
1800     URLCACHECONTAINER * pContainer;
1801     LPURLCACHE_HEADER pHeader;
1802     CACHEFILE_ENTRY * pEntry;
1803     URL_CACHEFILE_ENTRY * pUrlEntry;
1804     DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1805     DWORD dwOffsetLocalFileName = 0;
1806     DWORD dwOffsetHeader = 0;
1807     DWORD dwFileSizeLow = 0;
1808     DWORD dwFileSizeHigh = 0;
1809     BYTE cDirectory = 0;
1810     char achFile[MAX_PATH];
1811     char achUrl[MAX_PATH];
1812     char *pchLocalFileName = 0;
1813
1814     TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n",
1815         debugstr_w(lpszUrlName),
1816         debugstr_w(lpszLocalFileName),
1817         CacheEntryType,
1818         lpHeaderInfo,
1819         dwHeaderSize,
1820         debugstr_w(lpszFileExtension),
1821         debugstr_w(lpszOriginalUrl));
1822
1823     if (lpszOriginalUrl)
1824         WARN(": lpszOriginalUrl ignored\n");
1825  
1826     if (lpszLocalFileName)
1827     {
1828         HANDLE hFile;
1829
1830         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
1831         if (hFile == INVALID_HANDLE_VALUE)
1832         {
1833             ERR("couldn't open file %s (error is %ld)\n", debugstr_w(lpszLocalFileName), GetLastError());
1834             return FALSE;
1835         }
1836
1837         /* Get file size */
1838         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1839         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
1840         {
1841             ERR("couldn't get file size (error is %ld)\n", GetLastError());
1842             CloseHandle(hFile);
1843             return FALSE;
1844         }
1845
1846         CloseHandle(hFile);
1847     }
1848
1849     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1850         return FALSE;
1851
1852     if (!URLCacheContainer_OpenIndex(pContainer))
1853         return FALSE;
1854
1855     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1856         return FALSE;
1857
1858     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
1859
1860     if (URLCache_FindEntryInHash(pHeader, achUrl, &pEntry))
1861     {
1862         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1863         FIXME("entry already in cache - don't know what to do!\n");
1864 /*
1865  *        SetLastError(ERROR_FILE_NOT_FOUND);
1866  *        return FALSE;
1867  */
1868         return TRUE;
1869     }
1870
1871     if (lpszLocalFileName)
1872     {
1873         BOOL bFound = FALSE;
1874
1875         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
1876         {
1877             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1878             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
1879             SetLastError(ERROR_INVALID_PARAMETER);
1880             return FALSE;
1881         }
1882
1883         /* skip container path prefix */
1884         lpszLocalFileName += lstrlenW(pContainer->path);
1885
1886         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
1887         pchLocalFileName = achFile;
1888
1889         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1890         {
1891             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
1892             {
1893                 bFound = TRUE;
1894                 break;
1895             }
1896         }
1897
1898         if (!bFound)
1899         {
1900             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1901             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
1902             SetLastError(ERROR_INVALID_PARAMETER);
1903             return FALSE;
1904         }
1905
1906         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1907     }
1908
1909     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
1910     if (lpszLocalFileName)
1911     {
1912         dwOffsetLocalFileName = dwBytesNeeded;
1913         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
1914     }
1915     if (lpHeaderInfo)
1916     {
1917         dwOffsetHeader = dwBytesNeeded;
1918         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1919     }
1920
1921     /* round up to next block */
1922     if (dwBytesNeeded % BLOCKSIZE)
1923     {
1924         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1925         dwBytesNeeded += BLOCKSIZE;
1926     }
1927
1928     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1929     {
1930         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931         ERR("no free entries\n");
1932         return FALSE;
1933     }
1934
1935     /* FindFirstFreeEntry fills in blocks used */
1936     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1937     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1938     pUrlEntry->CacheDir = cDirectory;
1939     pUrlEntry->CacheEntryType = CacheEntryType;
1940     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1941     pUrlEntry->dwExemptDelta = 0;
1942     pUrlEntry->dwHitRate = 0;
1943     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1944     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1945     pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1946     pUrlEntry->dwSizeHigh = 0;
1947     pUrlEntry->dwSizeLow = dwFileSizeLow;
1948     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1949     pUrlEntry->dwUseCount = 0;
1950     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1951     pUrlEntry->LastModifiedTime = LastModifiedTime;
1952     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1953     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1954     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1955     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1956
1957     /*** Unknowns ***/
1958     pUrlEntry->dwUnknown1 = 0;
1959     pUrlEntry->dwUnknown2 = 0;
1960     pUrlEntry->dwUnknown3 = 0x60;
1961     pUrlEntry->Unknown4 = 0;
1962     pUrlEntry->wUnknown5 = 0x1010;
1963     pUrlEntry->dwUnknown6 = 0;
1964     pUrlEntry->dwUnknown7 = 0;
1965     pUrlEntry->dwUnknown8 = 0;
1966
1967
1968     strcpy(pUrlEntry->szSourceUrlName, achUrl);
1969     if (dwOffsetLocalFileName)
1970         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
1971     if (dwOffsetHeader)
1972         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
1973
1974     if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1975     {
1976         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1977         return FALSE;
1978     }
1979
1980     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1981
1982     return TRUE;
1983 }
1984
1985 /***********************************************************************
1986  *           CommitUrlCacheEntryA (WININET.@)
1987  *
1988  */
1989 BOOL WINAPI CommitUrlCacheEntryA(
1990     IN LPCSTR lpszUrlName,
1991     IN LPCSTR lpszLocalFileName,
1992     IN FILETIME ExpireTime,
1993     IN FILETIME LastModifiedTime,
1994     IN DWORD CacheEntryType,
1995     IN LPBYTE lpHeaderInfo,
1996     IN DWORD dwHeaderSize,
1997     IN LPCSTR lpszFileExtension,
1998     IN LPCSTR lpszOriginalUrl
1999     )
2000 {
2001     DWORD len;
2002     WCHAR *url_name;
2003     WCHAR *local_file_name;
2004     WCHAR *original_url = NULL;
2005     BOOL bSuccess = FALSE;
2006     DWORD dwError = 0;
2007
2008     TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n",
2009         debugstr_a(lpszUrlName),
2010         debugstr_a(lpszLocalFileName),
2011         CacheEntryType,
2012         lpHeaderInfo,
2013         dwHeaderSize,
2014         debugstr_a(lpszFileExtension),
2015         debugstr_a(lpszOriginalUrl));
2016
2017     if (lpszFileExtension != 0)
2018     {
2019         SetLastError(ERROR_INVALID_PARAMETER);
2020         return FALSE;
2021     }
2022     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2023         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2024     {
2025         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2026         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2027             (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2028         {
2029             MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2030             if (!lpszOriginalUrl ||
2031                 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2032                   (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2033             {
2034                 if (original_url)
2035                     MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2036                 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2037                                         CacheEntryType, lpHeaderInfo, dwHeaderSize,
2038                                         NULL, original_url))
2039                 {
2040                     bSuccess = TRUE;
2041                 }
2042                 else
2043                 {
2044                 dwError = GetLastError();
2045                 }
2046                 if (original_url)
2047                 HeapFree(GetProcessHeap(), 0, original_url);
2048             }
2049                 else
2050             {
2051                 dwError = GetLastError();
2052             }
2053             HeapFree(GetProcessHeap(), 0, local_file_name);
2054         }
2055         else
2056         {
2057             dwError = GetLastError();
2058         }
2059         HeapFree(GetProcessHeap(), 0, url_name);
2060         if (!bSuccess)
2061             SetLastError(dwError);
2062     }
2063     return bSuccess;
2064 }
2065
2066 /***********************************************************************
2067  *           CommitUrlCacheEntryW (WININET.@)
2068  *
2069  */
2070 BOOL WINAPI CommitUrlCacheEntryW(
2071     IN LPCWSTR lpszUrlName,
2072     IN LPCWSTR lpszLocalFileName,
2073     IN FILETIME ExpireTime,
2074     IN FILETIME LastModifiedTime,
2075     IN DWORD CacheEntryType,
2076     IN LPWSTR lpHeaderInfo,
2077     IN DWORD dwHeaderSize,
2078     IN LPCWSTR lpszFileExtension,
2079     IN LPCWSTR lpszOriginalUrl
2080     )
2081 {
2082     DWORD dwError = 0;
2083     BOOL bSuccess = FALSE;
2084     DWORD len = 0;
2085     CHAR *header_info = NULL;
2086
2087     TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n",
2088         debugstr_w(lpszUrlName),
2089         debugstr_w(lpszLocalFileName),
2090         CacheEntryType,
2091         lpHeaderInfo,
2092         dwHeaderSize,
2093         debugstr_w(lpszFileExtension),
2094         debugstr_w(lpszOriginalUrl));
2095
2096     if (!lpHeaderInfo ||
2097         ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2098          (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2099     {
2100         if (header_info)
2101             WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2102         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2103                                 CacheEntryType, header_info, len, lpszFileExtension, lpszOriginalUrl))
2104         {
2105                 bSuccess = TRUE;
2106         }
2107         else
2108         {
2109                 dwError = GetLastError();
2110         }
2111         if (header_info)
2112         {
2113             HeapFree(GetProcessHeap(), 0, header_info);
2114             if (!bSuccess)
2115                 SetLastError(dwError);
2116         }
2117     }
2118     return bSuccess;
2119 }
2120
2121 /***********************************************************************
2122  *           ReadUrlCacheEntryStream (WININET.@)
2123  *
2124  */
2125 BOOL WINAPI ReadUrlCacheEntryStream(
2126     IN HANDLE hUrlCacheStream,
2127     IN  DWORD dwLocation,
2128     IN OUT LPVOID lpBuffer,
2129     IN OUT LPDWORD lpdwLen,
2130     IN DWORD dwReserved
2131     )
2132 {
2133     /* Get handle to file from 'stream' */
2134     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2135
2136     if (dwReserved != 0)
2137     {
2138         ERR("dwReserved != 0\n");
2139         SetLastError(ERROR_INVALID_PARAMETER);
2140         return FALSE;
2141     }
2142
2143     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2144     {
2145         SetLastError(ERROR_INVALID_HANDLE);
2146         return FALSE;
2147     }
2148
2149     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2150         return FALSE;
2151     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2152 }
2153
2154 /***********************************************************************
2155  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2156  *
2157  */
2158 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2159     IN LPCSTR lpszUrlName,
2160     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2161     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2162     IN BOOL fRandomRead,
2163     IN DWORD dwReserved
2164     )
2165 {
2166     /* NOTE: this is not the same as the way that the native
2167      * version allocates 'stream' handles. I did it this way
2168      * as it is much easier and no applications should depend
2169      * on this behaviour. (Native version appears to allocate
2170      * indices into a table)
2171      */
2172     STREAM_HANDLE * pStream;
2173     HANDLE hFile;
2174
2175     TRACE( "(%s, %p, %p, %x, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2176            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2177
2178     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2179         lpCacheEntryInfo,
2180         lpdwCacheEntryInfoBufferSize,
2181         dwReserved))
2182     {
2183         return NULL;
2184     }
2185
2186     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2187         GENERIC_READ,
2188         FILE_SHARE_READ,
2189         NULL,
2190         OPEN_EXISTING,
2191         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2192         NULL);
2193     if (hFile == INVALID_HANDLE_VALUE)
2194         return FALSE;
2195     
2196     /* allocate handle storage space */
2197     pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2198     if (!pStream)
2199     {
2200         CloseHandle(hFile);
2201         SetLastError(ERROR_OUTOFMEMORY);
2202         return FALSE;
2203     }
2204
2205     pStream->hFile = hFile;
2206     strcpy(pStream->lpszUrl, lpszUrlName);
2207     return (HANDLE)pStream;
2208 }
2209
2210 /***********************************************************************
2211  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2212  *
2213  */
2214 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2215     IN LPCWSTR lpszUrlName,
2216     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2217     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2218     IN BOOL fRandomRead,
2219     IN DWORD dwReserved
2220     )
2221 {
2222     FIXME( "(%s, %p, %p, %x, 0x%08lx)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2223            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2224     return NULL;
2225 }
2226
2227 /***********************************************************************
2228  *           UnlockUrlCacheEntryStream (WININET.@)
2229  *
2230  */
2231 BOOL WINAPI UnlockUrlCacheEntryStream(
2232     IN HANDLE hUrlCacheStream,
2233     IN DWORD dwReserved
2234 )
2235 {
2236     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2237
2238     if (dwReserved != 0)
2239     {
2240         ERR("dwReserved != 0\n");
2241         SetLastError(ERROR_INVALID_PARAMETER);
2242         return FALSE;
2243     }
2244
2245     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2246     {
2247         SetLastError(ERROR_INVALID_HANDLE);
2248         return FALSE;
2249     }
2250
2251     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2252         return FALSE;
2253
2254     /* close file handle */
2255     CloseHandle(pStream->hFile);
2256
2257     /* free allocated space */
2258     HeapFree(GetProcessHeap(), 0, pStream);
2259
2260     return TRUE;
2261 }
2262
2263
2264 /***********************************************************************
2265  *           DeleteUrlCacheEntryA (WININET.@)
2266  *
2267  */
2268 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2269 {
2270     URLCACHECONTAINER * pContainer;
2271     LPURLCACHE_HEADER pHeader;
2272     CACHEFILE_ENTRY * pEntry;
2273     DWORD dwStartBlock;
2274     DWORD dwBlock;
2275     BYTE * AllocationTable;
2276
2277     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2278
2279     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2280         return FALSE;
2281
2282     if (!URLCacheContainer_OpenIndex(pContainer))
2283         return FALSE;
2284
2285     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2286         return FALSE;
2287
2288     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2289     {
2290         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2291         TRACE("entry %s not found!\n", lpszUrlName);
2292         SetLastError(ERROR_FILE_NOT_FOUND);
2293         return FALSE;
2294     }
2295
2296     AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
2297
2298     /* update allocation table */
2299     dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
2300     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
2301         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
2302
2303     URLCache_DeleteEntry(pEntry);
2304
2305     URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2306
2307     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2308
2309     return TRUE;
2310 }
2311
2312 /***********************************************************************
2313  *           DeleteUrlCacheEntryW (WININET.@)
2314  *
2315  */
2316 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2317 {
2318     FIXME("(%s) stub\n", debugstr_w(lpszUrlName));
2319     return TRUE;
2320 }
2321
2322 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2323 {
2324     FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
2325     return TRUE;
2326 }
2327
2328 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2329 {
2330     FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
2331     return TRUE;
2332 }
2333
2334 /***********************************************************************
2335  *           CreateCacheContainerA (WININET.@)
2336  */
2337 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2338                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2339 {
2340     FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
2341           d1, d2, d3, d4, d5, d6, d7, d8);
2342     return TRUE;
2343 }
2344
2345 /***********************************************************************
2346  *           CreateCacheContainerW (WININET.@)
2347  */
2348 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2349                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2350 {
2351     FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
2352           d1, d2, d3, d4, d5, d6, d7, d8);
2353     return TRUE;
2354 }
2355
2356 /***********************************************************************
2357  *           FindCloseUrlCache (WININET.@)
2358  */
2359 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2360 {
2361     FIXME("(%p) stub\n", hEnumHandle);
2362     return TRUE;
2363 }
2364
2365 /***********************************************************************
2366  *           FindFirstUrlCacheContainerA (WININET.@)
2367  */
2368 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2369 {
2370     FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
2371     return NULL;
2372 }
2373
2374 /***********************************************************************
2375  *           FindFirstUrlCacheContainerW (WININET.@)
2376  */
2377 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2378 {
2379     FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
2380     return NULL;
2381 }
2382
2383 /***********************************************************************
2384  *           FindNextUrlCacheContainerA (WININET.@)
2385  */
2386 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2387 {
2388     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2389     return FALSE;
2390 }
2391
2392 /***********************************************************************
2393  *           FindNextUrlCacheContainerW (WININET.@)
2394  */
2395 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2396 {
2397     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2398     return FALSE;
2399 }
2400
2401 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2402   LPCSTR lpszUrlSearchPattern,
2403   DWORD dwFlags,
2404   DWORD dwFilter,
2405   GROUPID GroupId,
2406   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2407   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2408   LPVOID lpReserved,
2409   LPDWORD pcbReserved2,
2410   LPVOID lpReserved3
2411 )
2412 {
2413     FIXME("(%s, 0x%08lx, 0x%08lx, 0x%08lx%08lx, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2414           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2415           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2416     SetLastError(ERROR_FILE_NOT_FOUND);
2417     return NULL;
2418 }
2419
2420 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2421   LPCWSTR lpszUrlSearchPattern,
2422   DWORD dwFlags,
2423   DWORD dwFilter,
2424   GROUPID GroupId,
2425   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2426   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2427   LPVOID lpReserved,
2428   LPDWORD pcbReserved2,
2429   LPVOID lpReserved3
2430 )
2431 {
2432     FIXME("(%s, 0x%08lx, 0x%08lx, 0x%08lx%08lx, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2433           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2434           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2435     SetLastError(ERROR_FILE_NOT_FOUND);
2436     return NULL;
2437 }
2438
2439 /***********************************************************************
2440  *           FindFirstUrlCacheEntryA (WININET.@)
2441  *
2442  */
2443 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2444  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2445 {
2446   FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2447   SetLastError(ERROR_FILE_NOT_FOUND);
2448   return 0;
2449 }
2450
2451 /***********************************************************************
2452  *           FindFirstUrlCacheEntryW (WININET.@)
2453  *
2454  */
2455 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2456  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2457 {
2458   FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2459   return 0;
2460 }
2461
2462 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2463                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2464 {
2465     FIXME("(0x%08lx, 0x%08lx, %p, 0x%08lx, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2466           dwSearchCondition, lpGroupId, lpReserved);
2467     return NULL;
2468 }
2469
2470 BOOL WINAPI FindNextUrlCacheEntryA(
2471   HANDLE hEnumHandle,
2472   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2473   LPDWORD lpdwNextCacheEntryInfoBufferSize
2474 )
2475 {
2476     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2477     return FALSE;
2478 }
2479
2480 BOOL WINAPI FindNextUrlCacheEntryW(
2481   HANDLE hEnumHandle,
2482   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2483   LPDWORD lpdwNextCacheEntryInfoBufferSize
2484 )
2485 {
2486     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2487     return FALSE;
2488 }
2489
2490 BOOL WINAPI FindNextUrlCacheEntryExA(
2491   HANDLE hEnumHandle,
2492   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2493   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2494   LPVOID lpReserved,
2495   LPDWORD pcbReserved2,
2496   LPVOID lpReserved3
2497 )
2498 {
2499     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2500           lpReserved, pcbReserved2, lpReserved3);
2501     return FALSE;
2502 }
2503
2504 BOOL WINAPI FindNextUrlCacheEntryExW(
2505   HANDLE hEnumHandle,
2506   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2507   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2508   LPVOID lpReserved,
2509   LPDWORD pcbReserved2,
2510   LPVOID lpReserved3
2511 )
2512 {
2513     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2514           lpReserved, pcbReserved2, lpReserved3);
2515     return FALSE;
2516 }
2517
2518 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2519 {
2520     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2521     return FALSE;
2522 }
2523
2524 /***********************************************************************
2525  *           CreateUrlCacheGroup (WININET.@)
2526  *
2527  */
2528 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2529 {
2530   FIXME("(0x%08lx, %p): stub\n", dwFlags, lpReserved);
2531   return FALSE;
2532 }
2533
2534 /***********************************************************************
2535  *           DeleteUrlCacheGroup (WININET.@)
2536  *
2537  */
2538 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2539 {
2540     FIXME("(0x%08lx%08lx, 0x%08lx, %p) stub\n",
2541           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2542     return FALSE;
2543 }
2544
2545 /***********************************************************************
2546  *           SetUrlCacheEntryGroupA (WININET.@)
2547  *
2548  */
2549 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2550   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2551   LPVOID lpReserved)
2552 {
2553     FIXME("(%s, 0x%08lx, 0x%08lx%08lx, %p, 0x%08lx, %p) stub\n",
2554           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2555           pbGroupAttributes, cbGroupAttributes, lpReserved);
2556     SetLastError(ERROR_FILE_NOT_FOUND);
2557     return FALSE;
2558 }
2559
2560 /***********************************************************************
2561  *           SetUrlCacheEntryGroupW (WININET.@)
2562  *
2563  */
2564 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2565   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2566   LPVOID lpReserved)
2567 {
2568     FIXME("(%s, 0x%08lx, 0x%08lx%08lx, %p, 0x%08lx, %p) stub\n",
2569           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2570           pbGroupAttributes, cbGroupAttributes, lpReserved);
2571     SetLastError(ERROR_FILE_NOT_FOUND);
2572     return FALSE;
2573 }
2574
2575 /***********************************************************************
2576  *           GetUrlCacheConfigInfoW (WININET.@)
2577  */
2578 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2579 {
2580     FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2581     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2582     return FALSE;
2583 }
2584
2585 /***********************************************************************
2586  *           GetUrlCacheConfigInfoA (WININET.@)
2587  *
2588  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2589  */
2590 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2591 {
2592     FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2593     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2594     return FALSE;
2595 }
2596
2597 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2598                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2599                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2600 {
2601     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
2602           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2603           lpdwGroupInfo, lpReserved);
2604     return FALSE;
2605 }
2606
2607 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2608                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2609                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2610 {
2611     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
2612           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2613           lpdwGroupInfo, lpReserved);
2614     return FALSE;
2615 }
2616
2617 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2618                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2619 {
2620     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p) stub\n",
2621           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2622     return TRUE;
2623 }
2624
2625 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2626                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2627 {
2628     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p) stub\n",
2629           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2630     return TRUE;
2631 }
2632
2633 BOOL WINAPI SetUrlCacheConfigInfoA( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2634 {
2635     FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
2636     return TRUE;
2637 }
2638
2639 BOOL WINAPI SetUrlCacheConfigInfoW( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2640 {
2641     FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
2642     return TRUE;
2643 }
2644
2645 /***********************************************************************
2646  *           DeleteIE3Cache (WININET.@)
2647  *
2648  * Deletes the files used by the IE3 URL caching system.
2649  *
2650  * PARAMS
2651  *   hWnd        [I] A dummy window.
2652  *   hInst       [I] Instance of process calling the function.
2653  *   lpszCmdLine [I] Options used by function.
2654  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
2655  *
2656  * RETURNS
2657  *   nothing
2658  */
2659 void WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2660 {
2661     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
2662 }