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