Fixed some calling conventions.
[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     URLCACHECONTAINER * pContainer;
1598     LPURLCACHE_HEADER pHeader;
1599     CHAR szFile[MAX_PATH];
1600     CHAR szExtension[MAX_PATH];
1601     LPCSTR lpszUrlPart;
1602     LPCSTR lpszUrlEnd;
1603     LPCSTR lpszFileNameExtension;
1604     LPSTR lpszFileNameNoPath;
1605     int i;
1606     int countnoextension;
1607     BYTE CacheDir;
1608     LONG lBufferSize;
1609     BOOL bFound = FALSE;
1610     int count;
1611
1612     TRACE("(%s, 0x%08lx, %s, %p, 0x%08lx)\n",
1613         debugstr_a(lpszUrlName),
1614         dwExpectedFileSize,
1615         debugstr_a(lpszFileExtension),
1616         lpszFileName,
1617         dwReserved);
1618
1619     if (dwReserved)
1620     {
1621         ERR("dwReserved != 0\n");
1622         SetLastError(ERROR_INVALID_PARAMETER);
1623         return FALSE;
1624     }
1625
1626     for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1627         ;
1628     
1629     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1630         lpszUrlEnd--;
1631
1632     for (lpszUrlPart = lpszUrlEnd; 
1633         (lpszUrlPart >= lpszUrlName); 
1634         lpszUrlPart--)
1635     {
1636         if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1637         {
1638             bFound = TRUE;
1639             lpszUrlPart++;
1640             break;
1641         }
1642     }
1643     if (!strcmp(lpszUrlPart, "www"))
1644     {
1645         lpszUrlPart += strlen("www");
1646     }
1647
1648     count = lpszUrlEnd - lpszUrlPart;
1649
1650     if (bFound && (count < MAX_PATH))
1651     {
1652         memcpy(szFile, lpszUrlPart, count * sizeof(CHAR));
1653         szFile[count] = '\0';
1654         /* FIXME: get rid of illegal characters like \, / and : */
1655     }
1656     else
1657     {
1658         FIXME("need to generate a random filename\n");
1659     }
1660
1661     TRACE("File name: %s\n", szFile);
1662
1663     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1664         return FALSE;
1665
1666     if (!URLCacheContainer_OpenIndex(pContainer))
1667         return FALSE;
1668
1669     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1670         return FALSE;
1671
1672     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1673
1674     lBufferSize = MAX_PATH * sizeof(CHAR);
1675     URLCache_LocalFileNameToPathA(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1676
1677     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1678
1679     lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(CHAR) + DIR_LENGTH + 1;
1680
1681     countnoextension = strlen(lpszFileNameNoPath);
1682     lpszFileNameExtension = PathFindExtensionA(lpszFileNameNoPath);
1683     if (lpszFileNameExtension)
1684         countnoextension -= strlen(lpszFileNameExtension);
1685     *szExtension = '\0';
1686
1687     if (lpszFileExtension)
1688     {
1689         szExtension[0] = '.';
1690         strcpy(szExtension+1, lpszFileExtension);
1691     }
1692
1693     for (i = 0; i < 255; i++)
1694     {
1695         HANDLE hFile;
1696         strncpy(lpszFileNameNoPath, szFile, countnoextension);
1697         sprintf(lpszFileNameNoPath + countnoextension, "[%u]%s", i, szExtension);
1698         TRACE("Trying: %s\n", lpszFileName);
1699         hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1700         if (hFile != INVALID_HANDLE_VALUE)
1701         {
1702             CloseHandle(hFile);
1703             return TRUE;
1704         }
1705     }
1706
1707     return FALSE;
1708 }
1709
1710 /***********************************************************************
1711  *           CreateUrlCacheEntryW (WININET.@)
1712  *
1713  */
1714 BOOL WINAPI CreateUrlCacheEntryW(
1715     IN LPCWSTR lpszUrlName,
1716     IN DWORD dwExpectedFileSize,
1717     IN LPCWSTR lpszFileExtension,
1718     OUT LPWSTR lpszFileName,
1719     IN DWORD dwReserved
1720 )
1721 {
1722     FIXME("(%s, 0x%08lx, %s, %p, 0x%08lx) stub\n",
1723         debugstr_w(lpszUrlName),
1724         dwExpectedFileSize,
1725         debugstr_w(lpszFileExtension),
1726         lpszFileName,
1727         dwReserved);
1728
1729     return TRUE;
1730 }
1731
1732 /***********************************************************************
1733  *           CommitUrlCacheEntryA (WININET.@)
1734  *
1735  */
1736 BOOL WINAPI CommitUrlCacheEntryA(
1737     IN LPCSTR lpszUrlName,
1738     IN LPCSTR lpszLocalFileName,
1739     IN FILETIME ExpireTime,
1740     IN FILETIME LastModifiedTime,
1741     IN DWORD CacheEntryType,
1742     IN LPBYTE lpHeaderInfo,
1743     IN DWORD dwHeaderSize,
1744     IN LPCSTR lpszFileExtension,
1745     IN LPCSTR dwReserved
1746     )
1747 {
1748     URLCACHECONTAINER * pContainer;
1749     LPURLCACHE_HEADER pHeader;
1750     CACHEFILE_ENTRY * pEntry;
1751     URL_CACHEFILE_ENTRY * pUrlEntry;
1752     DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1753     DWORD dwOffsetLocalFileName = 0;
1754     DWORD dwOffsetHeader = 0;
1755     DWORD dwFileSizeLow = 0;
1756     DWORD dwFileSizeHigh = 0;
1757     BYTE cDirectory = 0;
1758
1759     TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1760         debugstr_a(lpszUrlName),
1761         debugstr_a(lpszLocalFileName),
1762         CacheEntryType,
1763         lpHeaderInfo,
1764         dwHeaderSize,
1765         debugstr_a(lpszFileExtension),
1766         dwReserved);
1767
1768     if (dwReserved)
1769     {
1770         ERR("dwReserved != 0\n");
1771         SetLastError(ERROR_INVALID_PARAMETER);
1772         return FALSE;
1773     }
1774     if (lpHeaderInfo == NULL)
1775     {
1776         FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1777     }
1778  
1779     if (lpszLocalFileName)
1780     {
1781         HANDLE hFile;
1782         hFile = CreateFileA(lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1783         if (hFile == INVALID_HANDLE_VALUE)
1784         {
1785             ERR("couldn't open file %s (error is %ld)\n", debugstr_a(lpszLocalFileName), GetLastError());
1786             return FALSE;
1787         }
1788
1789         /* Get file size */
1790         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1791         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
1792         {
1793             ERR("couldn't get file size (error is %ld)\n", GetLastError());
1794             CloseHandle(hFile);
1795             return FALSE;
1796         }
1797
1798         CloseHandle(hFile);
1799     }
1800
1801     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1802         return FALSE;
1803
1804     if (!URLCacheContainer_OpenIndex(pContainer))
1805         return FALSE;
1806
1807     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1808         return FALSE;
1809
1810     if (URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1811     {
1812         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1813         FIXME("entry already in cache - don't know what to do!\n");
1814 /*
1815  *        SetLastError(ERROR_FILE_NOT_FOUND);
1816  *        return FALSE;
1817  */
1818         return TRUE;
1819     }
1820
1821     if (lpszLocalFileName)
1822     {
1823         BOOL bFound = FALSE;
1824         char szContainerPath[MAX_PATH];
1825         int container_path_len;
1826         container_path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szContainerPath, sizeof(szContainerPath), NULL, NULL);
1827         if (!container_path_len)
1828         {
1829             /* WideCharToMultiByte should have called SetLastError */
1830             return FALSE;
1831         }
1832
1833         if (strncmp(lpszLocalFileName, szContainerPath, container_path_len))
1834         {
1835             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1836             ERR("path %s must begin with cache content path %s\n", debugstr_a(lpszLocalFileName), debugstr_a(szContainerPath));
1837             SetLastError(ERROR_INVALID_PARAMETER);
1838             return FALSE;
1839         }
1840
1841         /* skip container path prefix */
1842         lpszLocalFileName += container_path_len;
1843
1844         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1845         {
1846             if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1847             {
1848                 bFound = TRUE;
1849                 break;
1850             }
1851         }
1852
1853         if (!bFound)
1854         {
1855             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1856             ERR("cache directory not found in path %s\n", lpszLocalFileName);
1857             SetLastError(ERROR_INVALID_PARAMETER);
1858             return FALSE;
1859         }
1860
1861         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1862     }
1863
1864     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlName) + 1);
1865     if (lpszLocalFileName)
1866     {
1867         dwOffsetLocalFileName = dwBytesNeeded;
1868         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszLocalFileName) + 1);
1869     }
1870     if (lpHeaderInfo)
1871     {
1872         dwOffsetHeader = dwBytesNeeded;
1873         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1874     }
1875
1876     /* round up to next block */
1877     if (dwBytesNeeded % BLOCKSIZE)
1878     {
1879         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1880         dwBytesNeeded += BLOCKSIZE;
1881     }
1882
1883     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1884     {
1885         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1886         ERR("no free entries\n");
1887         return FALSE;
1888     }
1889
1890     /* FindFirstFreeEntry fills in blocks used */
1891     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1892     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1893     pUrlEntry->CacheDir = cDirectory;
1894     pUrlEntry->CacheEntryType = CacheEntryType;
1895     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1896     pUrlEntry->dwExemptDelta = 0;
1897     pUrlEntry->dwHitRate = 0;
1898     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1899     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1900     pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1901     pUrlEntry->dwSizeHigh = 0;
1902     pUrlEntry->dwSizeLow = dwFileSizeLow;
1903     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1904     pUrlEntry->dwUseCount = 0;
1905     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1906     pUrlEntry->LastModifiedTime = LastModifiedTime;
1907     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1908     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1909     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1910     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1911
1912     /*** Unknowns ***/
1913     pUrlEntry->dwUnknown1 = 0;
1914     pUrlEntry->dwUnknown2 = 0;
1915     pUrlEntry->dwUnknown3 = 0x60;
1916     pUrlEntry->Unknown4 = 0;
1917     pUrlEntry->wUnknown5 = 0x1010;
1918     pUrlEntry->dwUnknown6 = 0;
1919     pUrlEntry->dwUnknown7 = 0;
1920     pUrlEntry->dwUnknown8 = 0;
1921
1922     strcpy(pUrlEntry->szSourceUrlName, lpszUrlName);
1923     if (dwOffsetLocalFileName)
1924         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), lpszLocalFileName);
1925     if (dwOffsetHeader)
1926         memcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetHeader), lpHeaderInfo, dwHeaderSize);
1927
1928     if (!URLCache_AddEntryToHash(pHeader, lpszUrlName, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1929     {
1930         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931         return FALSE;
1932     }
1933
1934     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1935
1936     return TRUE;
1937 }
1938
1939 /***********************************************************************
1940  *           CommitUrlCacheEntryW (WININET.@)
1941  *
1942  */
1943 BOOL WINAPI CommitUrlCacheEntryW(
1944     IN LPCWSTR lpszUrlName,
1945     IN LPCWSTR lpszLocalFileName,
1946     IN FILETIME ExpireTime,
1947     IN FILETIME LastModifiedTime,
1948     IN DWORD CacheEntryType,
1949     IN LPBYTE lpHeaderInfo,
1950     IN DWORD dwHeaderSize,
1951     IN LPCWSTR lpszFileExtension,
1952     IN LPCWSTR dwReserved
1953     )
1954 {
1955     FIXME("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p) stub\n",
1956         debugstr_w(lpszUrlName),
1957         debugstr_w(lpszLocalFileName),
1958         CacheEntryType,
1959         lpHeaderInfo,
1960         dwHeaderSize,
1961         debugstr_w(lpszFileExtension),
1962         dwReserved);
1963
1964     return TRUE;
1965 }
1966
1967 /***********************************************************************
1968  *           ReadUrlCacheEntryStream (WININET.@)
1969  *
1970  */
1971 BOOL WINAPI ReadUrlCacheEntryStream(
1972     IN HANDLE hUrlCacheStream,
1973     IN  DWORD dwLocation,
1974     IN OUT LPVOID lpBuffer,
1975     IN OUT LPDWORD lpdwLen,
1976     IN DWORD dwReserved
1977     )
1978 {
1979     /* Get handle to file from 'stream' */
1980     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1981
1982     if (dwReserved != 0)
1983     {
1984         ERR("dwReserved != 0\n");
1985         SetLastError(ERROR_INVALID_PARAMETER);
1986         return FALSE;
1987     }
1988
1989     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1990     {
1991         SetLastError(ERROR_INVALID_HANDLE);
1992         return FALSE;
1993     }
1994
1995     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
1996         return FALSE;
1997     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
1998 }
1999
2000 /***********************************************************************
2001  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2002  *
2003  */
2004 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2005     IN LPCSTR lpszUrlName,
2006     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2007     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2008     IN BOOL fRandomRead,
2009     IN DWORD dwReserved
2010     )
2011 {
2012     /* NOTE: this is not the same as the way that the native
2013      * version allocates 'stream' handles. I did it this way
2014      * as it is much easier and no applications should depend
2015      * on this behaviour. (Native version appears to allocate
2016      * indices into a table)
2017      */
2018     STREAM_HANDLE * pStream;
2019     HANDLE hFile;
2020
2021     TRACE( "(%s, %p, %p, %x, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2022            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2023
2024     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2025         lpCacheEntryInfo,
2026         lpdwCacheEntryInfoBufferSize,
2027         dwReserved))
2028     {
2029         return NULL;
2030     }
2031
2032     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2033         GENERIC_READ,
2034         FILE_SHARE_READ,
2035         NULL,
2036         OPEN_EXISTING,
2037         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2038         NULL);
2039     if (hFile == INVALID_HANDLE_VALUE)
2040         return FALSE;
2041     
2042     /* allocate handle storage space */
2043     pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2044     if (!pStream)
2045     {
2046         CloseHandle(hFile);
2047         SetLastError(ERROR_OUTOFMEMORY);
2048         return FALSE;
2049     }
2050
2051     pStream->hFile = hFile;
2052     strcpy(pStream->lpszUrl, lpszUrlName);
2053     return (HANDLE)pStream;
2054 }
2055
2056 /***********************************************************************
2057  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2058  *
2059  */
2060 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2061     IN LPCWSTR lpszUrlName,
2062     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2063     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2064     IN BOOL fRandomRead,
2065     IN DWORD dwReserved
2066     )
2067 {
2068     FIXME( "(%s, %p, %p, %x, 0x%08lx)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2069            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2070     return NULL;
2071 }
2072
2073 /***********************************************************************
2074  *           UnlockUrlCacheEntryStream (WININET.@)
2075  *
2076  */
2077 BOOL WINAPI UnlockUrlCacheEntryStream(
2078     IN HANDLE hUrlCacheStream,
2079     IN DWORD dwReserved
2080 )
2081 {
2082     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2083
2084     if (dwReserved != 0)
2085     {
2086         ERR("dwReserved != 0\n");
2087         SetLastError(ERROR_INVALID_PARAMETER);
2088         return FALSE;
2089     }
2090
2091     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2092     {
2093         SetLastError(ERROR_INVALID_HANDLE);
2094         return FALSE;
2095     }
2096
2097     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2098         return FALSE;
2099
2100     /* close file handle */
2101     CloseHandle(pStream->hFile);
2102
2103     /* free allocated space */
2104     HeapFree(GetProcessHeap(), 0, pStream);
2105
2106     return TRUE;
2107 }
2108
2109
2110 /***********************************************************************
2111  *           DeleteUrlCacheEntryA (WININET.@)
2112  *
2113  */
2114 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2115 {
2116     URLCACHECONTAINER * pContainer;
2117     LPURLCACHE_HEADER pHeader;
2118     CACHEFILE_ENTRY * pEntry;
2119     DWORD dwStartBlock;
2120     DWORD dwBlock;
2121     BYTE * AllocationTable;
2122
2123     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2124
2125     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2126         return FALSE;
2127
2128     if (!URLCacheContainer_OpenIndex(pContainer))
2129         return FALSE;
2130
2131     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2132         return FALSE;
2133
2134     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2135     {
2136         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2137         TRACE("entry %s not found!\n", lpszUrlName);
2138         SetLastError(ERROR_FILE_NOT_FOUND);
2139         return FALSE;
2140     }
2141
2142     AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
2143
2144     /* update allocation table */
2145     dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
2146     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
2147         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
2148
2149     URLCache_DeleteEntry(pEntry);
2150
2151     URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2152
2153     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2154
2155     return TRUE;
2156 }
2157
2158 /***********************************************************************
2159  *           DeleteUrlCacheEntryW (WININET.@)
2160  *
2161  */
2162 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2163 {
2164     FIXME("(%s) stub\n", debugstr_w(lpszUrlName));
2165     return TRUE;
2166 }
2167
2168 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2169 {
2170     FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
2171     return TRUE;
2172 }
2173
2174 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2175 {
2176     FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
2177     return TRUE;
2178 }
2179
2180 /***********************************************************************
2181  *           CreateCacheContainerA (WININET.@)
2182  */
2183 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2184                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2185 {
2186     FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
2187           d1, d2, d3, d4, d5, d6, d7, d8);
2188     return TRUE;
2189 }
2190
2191 /***********************************************************************
2192  *           CreateCacheContainerW (WININET.@)
2193  */
2194 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2195                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2196 {
2197     FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
2198           d1, d2, d3, d4, d5, d6, d7, d8);
2199     return TRUE;
2200 }
2201
2202 /***********************************************************************
2203  *           FindCloseUrlCache (WININET.@)
2204  */
2205 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2206 {
2207     FIXME("(%p) stub\n", hEnumHandle);
2208     return TRUE;
2209 }
2210
2211 /***********************************************************************
2212  *           FindFirstUrlCacheContainerA (WININET.@)
2213  */
2214 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2215 {
2216     FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
2217     return NULL;
2218 }
2219
2220 /***********************************************************************
2221  *           FindFirstUrlCacheContainerW (WININET.@)
2222  */
2223 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2224 {
2225     FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
2226     return NULL;
2227 }
2228
2229 /***********************************************************************
2230  *           FindNextUrlCacheContainerA (WININET.@)
2231  */
2232 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2233 {
2234     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2235     return FALSE;
2236 }
2237
2238 /***********************************************************************
2239  *           FindNextUrlCacheContainerW (WININET.@)
2240  */
2241 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2242 {
2243     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2244     return FALSE;
2245 }
2246
2247 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2248   LPCSTR lpszUrlSearchPattern,
2249   DWORD dwFlags,
2250   DWORD dwFilter,
2251   GROUPID GroupId,
2252   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2253   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2254   LPVOID lpReserved,
2255   LPDWORD pcbReserved2,
2256   LPVOID lpReserved3
2257 )
2258 {
2259     FIXME("(%s, 0x%08lx, 0x%08lx, 0x%08lx%08lx, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2260           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2261           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2262     SetLastError(ERROR_FILE_NOT_FOUND);
2263     return NULL;
2264 }
2265
2266 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2267   LPCWSTR lpszUrlSearchPattern,
2268   DWORD dwFlags,
2269   DWORD dwFilter,
2270   GROUPID GroupId,
2271   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2272   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2273   LPVOID lpReserved,
2274   LPDWORD pcbReserved2,
2275   LPVOID lpReserved3
2276 )
2277 {
2278     FIXME("(%s, 0x%08lx, 0x%08lx, 0x%08lx%08lx, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2279           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2280           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2281     SetLastError(ERROR_FILE_NOT_FOUND);
2282     return NULL;
2283 }
2284
2285 /***********************************************************************
2286  *           FindFirstUrlCacheEntryA (WININET.@)
2287  *
2288  */
2289 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2290  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2291 {
2292   FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2293   SetLastError(ERROR_FILE_NOT_FOUND);
2294   return 0;
2295 }
2296
2297 /***********************************************************************
2298  *           FindFirstUrlCacheEntryW (WININET.@)
2299  *
2300  */
2301 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2302  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2303 {
2304   FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2305   return 0;
2306 }
2307
2308 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2309                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2310 {
2311     FIXME("(0x%08lx, 0x%08lx, %p, 0x%08lx, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2312           dwSearchCondition, lpGroupId, lpReserved);
2313     return NULL;
2314 }
2315
2316 BOOL WINAPI FindNextUrlCacheEntryA(
2317   HANDLE hEnumHandle,
2318   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2319   LPDWORD lpdwNextCacheEntryInfoBufferSize
2320 )
2321 {
2322     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2323     return FALSE;
2324 }
2325
2326 BOOL WINAPI FindNextUrlCacheEntryW(
2327   HANDLE hEnumHandle,
2328   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2329   LPDWORD lpdwNextCacheEntryInfoBufferSize
2330 )
2331 {
2332     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2333     return FALSE;
2334 }
2335
2336 BOOL WINAPI FindNextUrlCacheEntryExA(
2337   HANDLE hEnumHandle,
2338   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2339   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2340   LPVOID lpReserved,
2341   LPDWORD pcbReserved2,
2342   LPVOID lpReserved3
2343 )
2344 {
2345     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2346           lpReserved, pcbReserved2, lpReserved3);
2347     return FALSE;
2348 }
2349
2350 BOOL WINAPI FindNextUrlCacheEntryExW(
2351   HANDLE hEnumHandle,
2352   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2353   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2354   LPVOID lpReserved,
2355   LPDWORD pcbReserved2,
2356   LPVOID lpReserved3
2357 )
2358 {
2359     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2360           lpReserved, pcbReserved2, lpReserved3);
2361     return FALSE;
2362 }
2363
2364 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2365 {
2366     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2367     return FALSE;
2368 }
2369
2370 /***********************************************************************
2371  *           CreateUrlCacheGroup (WININET.@)
2372  *
2373  */
2374 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2375 {
2376   FIXME("(0x%08lx, %p): stub\n", dwFlags, lpReserved);
2377   return FALSE;
2378 }
2379
2380 /***********************************************************************
2381  *           DeleteUrlCacheGroup (WININET.@)
2382  *
2383  */
2384 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2385 {
2386     FIXME("(0x%08lx%08lx, 0x%08lx, %p) stub\n",
2387           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2388     return FALSE;
2389 }
2390
2391 /***********************************************************************
2392  *           SetUrlCacheEntryGroupA (WININET.@)
2393  *
2394  */
2395 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2396   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2397   LPVOID lpReserved)
2398 {
2399     FIXME("(%s, 0x%08lx, 0x%08lx%08lx, %p, 0x%08lx, %p) stub\n",
2400           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2401           pbGroupAttributes, cbGroupAttributes, lpReserved);
2402     SetLastError(ERROR_FILE_NOT_FOUND);
2403     return FALSE;
2404 }
2405
2406 /***********************************************************************
2407  *           SetUrlCacheEntryGroupW (WININET.@)
2408  *
2409  */
2410 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2411   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2412   LPVOID lpReserved)
2413 {
2414     FIXME("(%s, 0x%08lx, 0x%08lx%08lx, %p, 0x%08lx, %p) stub\n",
2415           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2416           pbGroupAttributes, cbGroupAttributes, lpReserved);
2417     SetLastError(ERROR_FILE_NOT_FOUND);
2418     return FALSE;
2419 }
2420
2421 /***********************************************************************
2422  *           GetUrlCacheConfigInfoW (WININET.@)
2423  */
2424 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2425 {
2426     FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2427     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2428     return FALSE;
2429 }
2430
2431 /***********************************************************************
2432  *           GetUrlCacheConfigInfoA (WININET.@)
2433  *
2434  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2435  */
2436 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2437 {
2438     FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2439     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2440     return FALSE;
2441 }
2442
2443 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2444                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2445                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2446 {
2447     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
2448           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2449           lpdwGroupInfo, lpReserved);
2450     return FALSE;
2451 }
2452
2453 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2454                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2455                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2456 {
2457     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
2458           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2459           lpdwGroupInfo, lpReserved);
2460     return FALSE;
2461 }
2462
2463 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2464                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2465 {
2466     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p) stub\n",
2467           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2468     return TRUE;
2469 }
2470
2471 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2472                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2473 {
2474     FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p) stub\n",
2475           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2476     return TRUE;
2477 }
2478
2479 BOOL WINAPI SetUrlCacheConfigInfoA( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2480 {
2481     FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
2482     return TRUE;
2483 }
2484
2485 BOOL WINAPI SetUrlCacheConfigInfoW( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2486 {
2487     FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
2488     return TRUE;
2489 }
2490
2491 /***********************************************************************
2492  *           DeleteIE3Cache (WININET.@)
2493  *
2494  * Deletes the files used by the IE3 URL caching system.
2495  *
2496  * PARAMS
2497  *   hWnd        [I] A dummy window.
2498  *   hInst       [I] Instance of process calling the function.
2499  *   lpszCmdLine [I] Options used by function.
2500  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
2501  *
2502  * RETURNS
2503  *   nothing
2504  */
2505 void WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2506 {
2507     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
2508 }