quartz: Stop DSound buffer playback when the filter is paused or stopped, not the...
[wine] / dlls / wininet / urlcache.c
1 /*
2  * Wininet - Url Cache functions
3  *
4  * Copyright 2001,2002 CodeWeavers
5  * Copyright 2003 Robert Shearman
6  *
7  * Eric Kohl
8  * Aric Stewart
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
36
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winuser.h"
40 #include "wininet.h"
41 #include "winerror.h"
42 #include "internet.h"
43 #include "winreg.h"
44 #include "shlwapi.h"
45 #include "wingdi.h"
46 #include "shlobj.h"
47
48 #include "wine/unicode.h"
49 #include "wine/list.h"
50 #include "wine/debug.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
53
54 #define ENTRY_START_OFFSET  0x4000
55 #define DIR_LENGTH          8
56 #define BLOCKSIZE           128
57 #define HASHTABLE_SIZE      448
58 #define HASHTABLE_BLOCKSIZE 7
59 #define HASHTABLE_FREE      3
60 #define ALLOCATION_TABLE_OFFSET 0x250
61 #define ALLOCATION_TABLE_SIZE   (0x1000 - ALLOCATION_TABLE_OFFSET)
62 #define HASHTABLE_NUM_ENTRIES   (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
63 #define NEWFILE_NUM_BLOCKS      0xd80
64 #define NEWFILE_SIZE            (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
65
66 #define DWORD_SIG(a,b,c,d)  (a | (b << 8) | (c << 16) | (d << 24))
67 #define URL_SIGNATURE   DWORD_SIG('U','R','L',' ')
68 #define REDR_SIGNATURE  DWORD_SIG('R','E','D','R')
69 #define LEAK_SIGNATURE  DWORD_SIG('L','E','A','K')
70 #define HASH_SIGNATURE  DWORD_SIG('H','A','S','H')
71
72 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
73
74 typedef struct _CACHEFILE_ENTRY
75 {
76 /*  union
77     {*/
78         DWORD dwSignature; /* e.g. "URL " */
79 /*      CHAR szSignature[4];
80     };*/
81     DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
82 } CACHEFILE_ENTRY;
83
84 typedef struct _URL_CACHEFILE_ENTRY
85 {
86     CACHEFILE_ENTRY CacheFileEntry;
87     FILETIME LastModifiedTime;
88     FILETIME LastAccessTime;
89     WORD wExpiredDate; /* expire date in dos format */
90     WORD wExpiredTime; /* expire time in dos format */
91     DWORD dwUnknown1; /* usually zero */
92     DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
93     DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
94     DWORD dwUnknown2; /* usually zero */
95     DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
96     DWORD dwUnknown3; /* usually 0x60 */
97     DWORD dwOffsetUrl; /* offset of start of url from start of entry */
98     BYTE CacheDir; /* index of cache directory this url is stored in */
99     BYTE Unknown4; /* usually zero */
100     WORD wUnknown5; /* usually 0x1010 */
101     DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
102     DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
103     DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
104     DWORD dwHeaderInfoSize;
105     DWORD dwUnknown6; /* usually zero */
106     WORD wLastSyncDate; /* last sync date in dos format */
107     WORD wLastSyncTime; /* last sync time in dos format */
108     DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
109     DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
110     WORD wUnknownDate; /* usually same as wLastSyncDate */
111     WORD wUnknownTime; /* usually same as wLastSyncTime */
112     DWORD dwUnknown7; /* usually zero */
113     DWORD dwUnknown8; /* usually zero */
114     /* packing to dword align start of next field */
115     /* CHAR szSourceUrlName[]; (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 %d)\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 %d)\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 %u\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         wszCachePath[path_len+1] = 0;
563
564         strcpyW(wszMutexName, wszCachePath);
565         
566         if (suffix_len)
567         {
568             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
569             wszCachePath[path_len + suffix_len + 1] = '\\';
570             wszCachePath[path_len + suffix_len + 2] = '\0';
571         }
572
573         URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
574     }
575 }
576
577 void URLCacheContainers_DeleteAll(void)
578 {
579     while(!list_empty(&UrlContainers))
580         URLCacheContainer_DeleteContainer(
581             LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
582         );
583 }
584
585 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
586 {
587     struct list * cursor;
588
589     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
590
591     LIST_FOR_EACH(cursor, &UrlContainers)
592     {
593         URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
594         int prefix_len = strlenW(pContainer->cache_prefix);
595         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
596         {
597             TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
598             *ppContainer = pContainer;
599             return TRUE;
600         }
601     }
602     ERR("no container found\n");
603     SetLastError(ERROR_FILE_NOT_FOUND);
604     return FALSE;
605 }
606
607 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
608 {
609     BOOL ret;
610     LPWSTR lpwszUrl;
611     int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
612     if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
613     {
614         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
615         ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
616         HeapFree(GetProcessHeap(), 0, lpwszUrl);
617         return ret;
618     }
619     return FALSE;
620 }
621
622 /***********************************************************************
623  *           URLCacheContainer_LockIndex (Internal)
624  *
625  */
626 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
627 {
628     BYTE index;
629     LPVOID pIndexData;
630     URLCACHE_HEADER * pHeader;
631
632     /* acquire mutex */
633     WaitForSingleObject(pContainer->hMutex, INFINITE);
634
635     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
636
637     if (!pIndexData)
638     {
639         ReleaseMutex(pContainer->hMutex);
640         ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
641         return FALSE;
642     }
643     pHeader = (URLCACHE_HEADER *)pIndexData;
644
645     /* file has grown - we need to remap to prevent us getting
646      * access violations when we try and access beyond the end
647      * of the memory mapped file */
648     if (pHeader->dwFileSize != pContainer->file_size)
649     {
650         URLCacheContainer_CloseIndex(pContainer);
651         if (!URLCacheContainer_OpenIndex(pContainer))
652         {
653             ReleaseMutex(pContainer->hMutex);
654             return FALSE;
655         }
656         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
657
658         if (!pIndexData)
659         {
660             ReleaseMutex(pContainer->hMutex);
661             ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
662             return FALSE;
663         }
664         pHeader = (URLCACHE_HEADER *)pIndexData;
665     }
666
667     TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
668
669     for (index = 0; index < pHeader->DirectoryCount; index++)
670     {
671         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
672     }
673     
674     return pHeader;
675 }
676
677 /***********************************************************************
678  *           URLCacheContainer_UnlockIndex (Internal)
679  *
680  */
681 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
682 {
683     /* release mutex */
684     ReleaseMutex(pContainer->hMutex);
685     return UnmapViewOfFile(pHeader);
686 }
687
688
689 #ifndef CHAR_BIT
690 #define CHAR_BIT    (8 * sizeof(CHAR))
691 #endif
692
693 /***********************************************************************
694  *           URLCache_Allocation_BlockIsFree (Internal)
695  *
696  *  Is the specified block number free?
697  *
698  * RETURNS
699  *    zero if free
700  *    non-zero otherwise
701  *
702  */
703 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
704 {
705     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
706     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
707 }
708
709 /***********************************************************************
710  *           URLCache_Allocation_BlockFree (Internal)
711  *
712  *  Marks the specified block as free
713  *
714  * RETURNS
715  *    nothing
716  *
717  */
718 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
719 {
720     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
721     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
722 }
723
724 /***********************************************************************
725  *           URLCache_Allocation_BlockAllocate (Internal)
726  *
727  *  Marks the specified block as allocated
728  *
729  * RETURNS
730  *    nothing
731  *
732  */
733 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
734 {
735     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
736     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
737 }
738
739 /***********************************************************************
740  *           URLCache_FindFirstFreeEntry (Internal)
741  *
742  *  Finds and allocates the first block of free space big enough and
743  * sets ppEntry to point to it.
744  *
745  * RETURNS
746  *    TRUE if it had enough space
747  *    FALSE if it couldn't find enough space
748  *
749  */
750 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
751 {
752     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
753     DWORD dwBlockNumber;
754     DWORD dwFreeCounter;
755     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
756     {
757         for (dwFreeCounter = 0; 
758             dwFreeCounter < dwBlocksNeeded &&
759               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
760               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
761             dwFreeCounter++)
762                 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
763
764         if (dwFreeCounter == dwBlocksNeeded)
765         {
766             DWORD index;
767             TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
768             for (index = 0; index < dwBlocksNeeded; index++)
769                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
770             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
771             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
772             return TRUE;
773         }
774     }
775     FIXME("Grow file\n");
776     return FALSE;
777 }
778
779 /***********************************************************************
780  *           URLCache_DeleteEntry (Internal)
781  *
782  *  Deletes the specified entry and frees the space allocated to it
783  *
784  * RETURNS
785  *    TRUE if it succeeded
786  *    FALSE if it failed
787  *
788  */
789 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
790 {
791     DWORD dwStartBlock;
792     DWORD dwBlock;
793     BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
794
795     /* update allocation table */
796     dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
797     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
798         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
799
800     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
801     return TRUE;
802 }
803
804 /***********************************************************************
805  *           URLCache_LocalFileNameToPathW (Internal)
806  *
807  *  Copies the full path to the specified buffer given the local file
808  * name and the index of the directory it is in. Always sets value in
809  * lpBufferSize to the required buffer size (in bytes).
810  *
811  * RETURNS
812  *    TRUE if the buffer was big enough
813  *    FALSE if the buffer was too small
814  *
815  */
816 static BOOL URLCache_LocalFileNameToPathW(
817     const URLCACHECONTAINER * pContainer,
818     LPCURLCACHE_HEADER pHeader,
819     LPCSTR szLocalFileName,
820     BYTE Directory,
821     LPWSTR wszPath,
822     LPLONG lpBufferSize)
823 {
824     LONG nRequired;
825     int path_len = strlenW(pContainer->path);
826     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
827     if (Directory >= pHeader->DirectoryCount)
828     {
829         *lpBufferSize = 0;
830         return FALSE;
831     }
832
833     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
834     if (nRequired < *lpBufferSize)
835     {
836         int dir_len;
837
838         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
839         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
840         wszPath[dir_len + path_len] = '\\';
841         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
842         *lpBufferSize = nRequired;
843         return TRUE;
844     }
845     *lpBufferSize = nRequired;
846     return FALSE;
847 }
848
849 /***********************************************************************
850  *           URLCache_LocalFileNameToPathA (Internal)
851  *
852  *  Copies the full path to the specified buffer given the local file
853  * name and the index of the directory it is in. Always sets value in
854  * lpBufferSize to the required buffer size.
855  *
856  * RETURNS
857  *    TRUE if the buffer was big enough
858  *    FALSE if the buffer was too small
859  *
860  */
861 static BOOL URLCache_LocalFileNameToPathA(
862     const URLCACHECONTAINER * pContainer,
863     LPCURLCACHE_HEADER pHeader,
864     LPCSTR szLocalFileName,
865     BYTE Directory,
866     LPSTR szPath,
867     LPLONG lpBufferSize)
868 {
869     LONG nRequired;
870     int path_len, file_name_len, dir_len;
871
872     if (Directory >= pHeader->DirectoryCount)
873     {
874         *lpBufferSize = 0;
875         return FALSE;
876     }
877
878     path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
879     file_name_len = strlen(szLocalFileName);
880     dir_len = DIR_LENGTH;
881
882     nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(WCHAR);
883     if (nRequired < *lpBufferSize)
884     {
885         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
886         memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
887         szPath[path_len + dir_len] = '\\';
888         memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
889         *lpBufferSize = nRequired;
890         return TRUE;
891     }
892     *lpBufferSize = nRequired;
893     return FALSE;
894 }
895
896 /***********************************************************************
897  *           URLCache_CopyEntry (Internal)
898  *
899  *  Copies an entry from the cache index file to the Win32 structure
900  *
901  * RETURNS
902  *    TRUE if the buffer was big enough
903  *    FALSE if the buffer was too small
904  *
905  */
906 static BOOL URLCache_CopyEntry(
907     URLCACHECONTAINER * pContainer,
908     LPCURLCACHE_HEADER pHeader, 
909     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
910     LPDWORD lpdwBufferSize, 
911     URL_CACHEFILE_ENTRY * pUrlEntry,
912     BOOL bUnicode)
913 {
914     int lenUrl;
915     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
916
917     if (*lpdwBufferSize >= dwRequiredSize)
918     {
919         lpCacheEntryInfo->lpHeaderInfo = NULL;
920         lpCacheEntryInfo->lpszFileExtension = NULL;
921         lpCacheEntryInfo->lpszLocalFileName = NULL;
922         lpCacheEntryInfo->lpszSourceUrlName = NULL;
923         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
924         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
925         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
926         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
927         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
928         lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
929         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
930         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
931         DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
932         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
933         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
934         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
935         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
936         DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
937     }
938
939     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
940         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
941     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
942     if (bUnicode)
943         lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
944     else
945         lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
946     dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
947
948     /* FIXME: is source url optional? */
949     if (*lpdwBufferSize >= dwRequiredSize)
950     {
951         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
952         if (bUnicode)
953             MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
954         else
955             memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
956     }
957
958     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
959         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
960     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
961
962     if (pUrlEntry->dwOffsetLocalName)
963     {
964         LONG nLocalFilePathSize;
965         LPSTR lpszLocalFileName;
966         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
967         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
968         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
969             URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
970         {
971             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
972         }
973         dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
974
975         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
976             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
977         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
978     }
979     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
980
981     if (*lpdwBufferSize >= dwRequiredSize)
982     {
983         lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
984         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
985         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
986     }
987     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
988         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
989     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
990
991     if (dwRequiredSize > *lpdwBufferSize)
992     {
993         *lpdwBufferSize = dwRequiredSize;
994         SetLastError(ERROR_INSUFFICIENT_BUFFER);
995         return FALSE;
996     }
997     *lpdwBufferSize = dwRequiredSize;
998     return TRUE;
999 }
1000
1001
1002 /***********************************************************************
1003  *           URLCache_SetEntryInfo (Internal)
1004  *
1005  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1006  * according the the flags set by dwFieldControl.
1007  *
1008  * RETURNS
1009  *    TRUE if the buffer was big enough
1010  *    FALSE if the buffer was too small
1011  *
1012  */
1013 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1014 {
1015     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1016         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1017     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1018         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1019     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1020         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1021     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1022         FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1023     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1024         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1025     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1026         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1027     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1028         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1029     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1030         FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1031
1032     return TRUE;
1033 }
1034
1035 /***********************************************************************
1036  *           URLCache_HashKey (Internal)
1037  *
1038  *  Returns the hash key for a given string
1039  *
1040  * RETURNS
1041  *    hash key for the string
1042  *
1043  */
1044 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1045 {
1046     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1047      * but the algorithm and result are not the same!
1048      */
1049     static const unsigned char lookupTable[256] = 
1050     {
1051         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1052         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1053         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1054         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1055         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1056         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1057         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1058         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1059         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1060         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1061         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1062         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1063         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1064         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1065         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1066         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1067         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1068         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1069         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1070         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1071         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1072         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1073         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1074         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1075         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1076         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1077         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1078         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1079         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1080         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1081         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1082         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1083     };
1084     BYTE key[4];
1085     DWORD i;
1086     int subscript[sizeof(key) / sizeof(key[0])];
1087
1088     subscript[0] = *lpszKey;
1089     subscript[1] = (char)(*lpszKey + 1);
1090     subscript[2] = (char)(*lpszKey + 2);
1091     subscript[3] = (char)(*lpszKey + 3);
1092
1093     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1094         key[i] = lookupTable[i];
1095
1096     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1097     {
1098         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1099             key[i] = lookupTable[*lpszKey ^ key[i]];
1100     }
1101
1102     return *(DWORD *)key;
1103 }
1104
1105 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1106 {
1107     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1108 }
1109
1110 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1111 {
1112     /* structure of hash table:
1113      *  448 entries divided into 64 blocks
1114      *  each block therefore contains a chain of 7 key/offset pairs
1115      * how position in table is calculated:
1116      *  1. the url is hashed in helper function
1117      *  2. the key % 64 * 8 is the offset
1118      *  3. the key in the hash table is the hash key aligned to 64
1119      *
1120      * note:
1121      *  there can be multiple hash tables in the file and the offset to
1122      *  the next one is stored in the header of the hash table
1123      */
1124     DWORD key = URLCache_HashKey(lpszUrl);
1125     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1126     HASH_CACHEFILE_ENTRY * pHashEntry;
1127     DWORD dwHashTableNumber = 0;
1128
1129     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1130
1131     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1132          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1133          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1134     {
1135         int i;
1136         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1137         {
1138             ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1139             continue;
1140         }
1141         /* make sure that it is in fact a hash entry */
1142         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1143         {
1144             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1145             continue;
1146         }
1147
1148         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1149         {
1150             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1151             if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1152             {
1153                 /* FIXME: we should make sure that this is the right element
1154                  * before returning and claiming that it is. We can do this
1155                  * by doing a simple compare between the URL we were given
1156                  * and the URL stored in the entry. However, this assumes
1157                  * we know the format of all the entries stored in the
1158                  * hash table */
1159                 *ppHashEntry = pHashElement;
1160                 return TRUE;
1161             }
1162         }
1163     }
1164     return FALSE;
1165 }
1166
1167 /***********************************************************************
1168  *           URLCache_FindEntryInHash (Internal)
1169  *
1170  *  Searches all the hash tables in the index for the given URL and
1171  * returns the entry, if it was found, in ppEntry
1172  *
1173  * RETURNS
1174  *    TRUE if the entry was found
1175  *    FALSE if the entry could not be found
1176  *
1177  */
1178 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
1179 {
1180     struct _HASH_ENTRY * pHashEntry;
1181     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1182     {
1183         *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1184         return TRUE;
1185     }
1186     return FALSE;
1187 }
1188
1189 /***********************************************************************
1190  *           URLCache_HashEntrySetUse (Internal)
1191  *
1192  *  Searches all the hash tables in the index for the given URL and
1193  * sets the use count (stored or'ed with key)
1194  *
1195  * RETURNS
1196  *    TRUE if the entry was found
1197  *    FALSE if the entry could not be found
1198  *
1199  */
1200 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1201 {
1202     struct _HASH_ENTRY * pHashEntry;
1203     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1204     {
1205         pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1206         return TRUE;
1207     }
1208     return FALSE;
1209 }
1210
1211 /***********************************************************************
1212  *           URLCache_DeleteEntryFromHash (Internal)
1213  *
1214  *  Searches all the hash tables in the index for the given URL and
1215  * then if found deletes the entry.
1216  *
1217  * RETURNS
1218  *    TRUE if the entry was found
1219  *    FALSE if the entry could not be found
1220  *
1221  */
1222 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1223 {
1224     struct _HASH_ENTRY * pHashEntry;
1225     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1226     {
1227         pHashEntry->dwHashKey = HASHTABLE_FREE;
1228         pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1229         return TRUE;
1230     }
1231     return FALSE;
1232 }
1233
1234 /***********************************************************************
1235  *           URLCache_AddEntryToHash (Internal)
1236  *
1237  *  Searches all the hash tables for a free slot based on the offset
1238  * generated from the hash key. If a free slot is found, the offset and
1239  * key are entered into the hash table.
1240  *
1241  * RETURNS
1242  *    TRUE if the entry was added
1243  *    FALSE if the entry could not be added
1244  *
1245  */
1246 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1247 {
1248     /* see URLCache_FindEntryInHash for structure of hash tables */
1249
1250     DWORD key = URLCache_HashKey(lpszUrl);
1251     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1252     HASH_CACHEFILE_ENTRY * pHashEntry;
1253     DWORD dwHashTableNumber = 0;
1254
1255     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1256
1257     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1258          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1259          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1260     {
1261         int i;
1262         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1263         {
1264             ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1265             break;
1266         }
1267         /* make sure that it is in fact a hash entry */
1268         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1269         {
1270             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1271             break;
1272         }
1273
1274         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1275         {
1276             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1277             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1278             {
1279                 pHashElement->dwHashKey = key;
1280                 pHashElement->dwOffsetEntry = dwOffsetEntry;
1281                 return TRUE;
1282             }
1283         }
1284     }
1285     pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1286     if (!pHashEntry)
1287         return FALSE;
1288
1289     pHashEntry->HashTable[offset].dwHashKey = key;
1290     pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1291     return TRUE;
1292 }
1293
1294 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1295 {
1296     HASH_CACHEFILE_ENTRY *pHash;
1297     DWORD dwOffset;
1298     int i;
1299
1300     if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1301     {
1302         FIXME("no free space for hash table\n");
1303         SetLastError(ERROR_DISK_FULL);
1304         return NULL;
1305     }
1306
1307     dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1308
1309     if (pPrevHash)
1310         pPrevHash->dwAddressNext = dwOffset;
1311     else
1312         pHeader->dwOffsetFirstHashTable = dwOffset;
1313     pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1314     pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1315     pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1316     for (i = 0; i < HASHTABLE_SIZE; i++)
1317     {
1318         pHash->HashTable[i].dwOffsetEntry = 0;
1319         pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1320     }
1321     return pHash;
1322 }
1323
1324 /***********************************************************************
1325  *           GetUrlCacheEntryInfoExA (WININET.@)
1326  *
1327  */
1328 BOOL WINAPI GetUrlCacheEntryInfoExA(
1329     LPCSTR lpszUrl,
1330     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1331     LPDWORD lpdwCacheEntryInfoBufSize,
1332     LPSTR lpszReserved,
1333     LPDWORD lpdwReserved,
1334     LPVOID lpReserved,
1335     DWORD dwFlags)
1336 {
1337     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1338         debugstr_a(lpszUrl), 
1339         lpCacheEntryInfo,
1340         lpdwCacheEntryInfoBufSize,
1341         lpszReserved,
1342         lpdwReserved,
1343         lpReserved,
1344         dwFlags);
1345
1346     if ((lpszReserved != NULL) ||
1347         (lpdwReserved != NULL) ||
1348         (lpReserved != NULL))
1349     {
1350         ERR("Reserved value was not 0\n");
1351         SetLastError(ERROR_INVALID_PARAMETER);
1352         return FALSE;
1353     }
1354     if (dwFlags != 0)
1355         FIXME("Undocumented flag(s): %x\n", dwFlags);
1356     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1357 }
1358
1359 /***********************************************************************
1360  *           GetUrlCacheEntryInfoA (WININET.@)
1361  *
1362  */
1363 BOOL WINAPI GetUrlCacheEntryInfoA(
1364     IN LPCSTR lpszUrlName,
1365     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1366     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1367 )
1368 {
1369     LPURLCACHE_HEADER pHeader;
1370     CACHEFILE_ENTRY * pEntry;
1371     URL_CACHEFILE_ENTRY * pUrlEntry;
1372     URLCACHECONTAINER * pContainer;
1373
1374     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1375
1376     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1377         return FALSE;
1378
1379     if (!URLCacheContainer_OpenIndex(pContainer))
1380         return FALSE;
1381
1382     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1383         return FALSE;
1384
1385     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1386     {
1387         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1388         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1389         SetLastError(ERROR_FILE_NOT_FOUND);
1390         return FALSE;
1391     }
1392
1393     if (pEntry->dwSignature != URL_SIGNATURE)
1394     {
1395         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1396         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1397         SetLastError(ERROR_FILE_NOT_FOUND);
1398         return FALSE;
1399     }
1400
1401     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1402     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1403     if (pUrlEntry->dwOffsetHeaderInfo)
1404         TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1405
1406     if (!URLCache_CopyEntry(
1407         pContainer,
1408         pHeader,
1409         lpCacheEntryInfo,
1410         lpdwCacheEntryInfoBufferSize,
1411         pUrlEntry,
1412         FALSE /* ANSI */))
1413     {
1414         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1415         return FALSE;
1416     }
1417     TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1418
1419     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1420
1421     return TRUE;
1422 }
1423
1424 /***********************************************************************
1425  *           GetUrlCacheEntryInfoW (WININET.@)
1426  *
1427  */
1428 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1429   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1430   LPDWORD lpdwCacheEntryInfoBufferSize)
1431 {
1432     LPURLCACHE_HEADER pHeader;
1433     CACHEFILE_ENTRY * pEntry;
1434     URL_CACHEFILE_ENTRY * pUrlEntry;
1435     URLCACHECONTAINER * pContainer;
1436     LPSTR lpszUrlA;
1437     int url_len;
1438
1439     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1440
1441     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1442     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1443     if (!lpszUrlA)
1444     {
1445         SetLastError(ERROR_OUTOFMEMORY);
1446         return FALSE;
1447     }
1448     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1449
1450     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1451     {
1452         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1453         return FALSE;
1454     }
1455
1456     if (!URLCacheContainer_OpenIndex(pContainer))
1457     {
1458         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1459         return FALSE;
1460     }
1461
1462     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1463     {
1464         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1465         return FALSE;
1466     }
1467
1468     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1469     {
1470         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1471         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1472         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1473         SetLastError(ERROR_FILE_NOT_FOUND);
1474         return FALSE;
1475     }
1476     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1477
1478     if (pEntry->dwSignature != URL_SIGNATURE)
1479     {
1480         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1481         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1482         SetLastError(ERROR_FILE_NOT_FOUND);
1483         return FALSE;
1484     }
1485
1486     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1487     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1488     TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1489
1490     if (!URLCache_CopyEntry(
1491         pContainer,
1492         pHeader,
1493         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1494         lpdwCacheEntryInfoBufferSize,
1495         pUrlEntry,
1496         TRUE /* UNICODE */))
1497     {
1498         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1499         return FALSE;
1500     }
1501     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1502
1503     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1504
1505     return TRUE;
1506 }
1507
1508 /***********************************************************************
1509  *           GetUrlCacheEntryInfoExW (WININET.@)
1510  *
1511  */
1512 BOOL WINAPI GetUrlCacheEntryInfoExW(
1513     LPCWSTR lpszUrl,
1514     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1515     LPDWORD lpdwCacheEntryInfoBufSize,
1516     LPWSTR lpszReserved,
1517     LPDWORD lpdwReserved,
1518     LPVOID lpReserved,
1519     DWORD dwFlags)
1520 {
1521     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1522         debugstr_w(lpszUrl), 
1523         lpCacheEntryInfo,
1524         lpdwCacheEntryInfoBufSize,
1525         lpszReserved,
1526         lpdwReserved,
1527         lpReserved,
1528         dwFlags);
1529
1530     if ((lpszReserved != NULL) ||
1531         (lpdwReserved != NULL) ||
1532         (lpReserved != NULL))
1533     {
1534         ERR("Reserved value was not 0\n");
1535         SetLastError(ERROR_INVALID_PARAMETER);
1536         return FALSE;
1537     }
1538     if (dwFlags != 0)
1539         FIXME("Undocumented flag(s): %x\n", dwFlags);
1540     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1541 }
1542
1543 /***********************************************************************
1544  *           SetUrlCacheEntryInfoA (WININET.@)
1545  */
1546 BOOL WINAPI SetUrlCacheEntryInfoA(
1547     LPCSTR lpszUrlName,
1548     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1549     DWORD dwFieldControl)
1550 {
1551     LPURLCACHE_HEADER pHeader;
1552     CACHEFILE_ENTRY * pEntry;
1553     URLCACHECONTAINER * pContainer;
1554
1555     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1556
1557     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1558         return FALSE;
1559
1560     if (!URLCacheContainer_OpenIndex(pContainer))
1561         return FALSE;
1562
1563     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1564         return FALSE;
1565
1566     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1567     {
1568         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1569         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1570         SetLastError(ERROR_FILE_NOT_FOUND);
1571         return FALSE;
1572     }
1573
1574     if (pEntry->dwSignature != URL_SIGNATURE)
1575     {
1576         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1577         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1578         SetLastError(ERROR_FILE_NOT_FOUND);
1579         return FALSE;
1580     }
1581
1582     URLCache_SetEntryInfo(
1583         (URL_CACHEFILE_ENTRY *)pEntry,
1584         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1585         dwFieldControl);
1586
1587     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1588
1589     return TRUE;
1590 }
1591
1592 /***********************************************************************
1593  *           SetUrlCacheEntryInfoW (WININET.@)
1594  */
1595 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1596 {
1597     LPURLCACHE_HEADER pHeader;
1598     CACHEFILE_ENTRY * pEntry;
1599     URLCACHECONTAINER * pContainer;
1600     LPSTR lpszUrlA;
1601     int url_len;
1602
1603     TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1604
1605     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1606     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1607     if (!lpszUrlA)
1608     {
1609         SetLastError(ERROR_OUTOFMEMORY);
1610         return FALSE;
1611     }
1612     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1613
1614     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1615     {
1616         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1617         return FALSE;
1618     }
1619
1620     if (!URLCacheContainer_OpenIndex(pContainer))
1621     {
1622         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1623         return FALSE;
1624     }
1625
1626     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1627     {
1628         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1629         return FALSE;
1630     }
1631
1632     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1633     {
1634         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1635         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1636         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1637         SetLastError(ERROR_FILE_NOT_FOUND);
1638         return FALSE;
1639     }
1640     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1641
1642     if (pEntry->dwSignature != URL_SIGNATURE)
1643     {
1644         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1645         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1646         SetLastError(ERROR_FILE_NOT_FOUND);
1647         return FALSE;
1648     }
1649
1650     URLCache_SetEntryInfo(
1651         (URL_CACHEFILE_ENTRY *)pEntry,
1652         lpCacheEntryInfo,
1653         dwFieldControl);
1654
1655     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1656
1657     return TRUE;
1658 }
1659
1660 /***********************************************************************
1661  *           RetrieveUrlCacheEntryFileA (WININET.@)
1662  *
1663  */
1664 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1665     IN LPCSTR lpszUrlName,
1666     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1667     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1668     IN DWORD dwReserved
1669     )
1670 {
1671     LPURLCACHE_HEADER pHeader;
1672     CACHEFILE_ENTRY * pEntry;
1673     URL_CACHEFILE_ENTRY * pUrlEntry;
1674     URLCACHECONTAINER * pContainer;
1675
1676     TRACE("(%s, %p, %p, 0x%08x)\n",
1677         debugstr_a(lpszUrlName),
1678         lpCacheEntryInfo,
1679         lpdwCacheEntryInfoBufferSize,
1680         dwReserved);
1681
1682     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1683         return FALSE;
1684
1685     if (!URLCacheContainer_OpenIndex(pContainer))
1686         return FALSE;
1687
1688     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1689         return FALSE;
1690
1691     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1692     {
1693         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1694         TRACE("entry %s not found!\n", lpszUrlName);
1695         SetLastError(ERROR_FILE_NOT_FOUND);
1696         return FALSE;
1697     }
1698
1699     if (pEntry->dwSignature != URL_SIGNATURE)
1700     {
1701         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1702         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1703         SetLastError(ERROR_FILE_NOT_FOUND);
1704         return FALSE;
1705     }
1706
1707     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1708     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1709     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1710
1711     pUrlEntry->dwHitRate++;
1712     pUrlEntry->dwUseCount++;
1713     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1714
1715     if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1716     {
1717         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1718         return FALSE;
1719     }
1720     TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1721
1722     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1723
1724     return TRUE;
1725 }
1726
1727 /***********************************************************************
1728  *           RetrieveUrlCacheEntryFileW (WININET.@)
1729  *
1730  */
1731 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1732     IN LPCWSTR lpszUrlName,
1733     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1734     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1735     IN DWORD dwReserved
1736     )
1737 {
1738     TRACE("(%s, %p, %p, 0x%08x)\n",
1739         debugstr_w(lpszUrlName),
1740         lpCacheEntryInfo,
1741         lpdwCacheEntryInfoBufferSize,
1742         dwReserved);
1743
1744     return FALSE;
1745 }
1746
1747 /***********************************************************************
1748  *           UnlockUrlCacheEntryFileA (WININET.@)
1749  *
1750  */
1751 BOOL WINAPI UnlockUrlCacheEntryFileA(
1752     IN LPCSTR lpszUrlName, 
1753     IN DWORD dwReserved
1754     )
1755 {
1756     LPURLCACHE_HEADER pHeader;
1757     CACHEFILE_ENTRY * pEntry;
1758     URL_CACHEFILE_ENTRY * pUrlEntry;
1759     URLCACHECONTAINER * pContainer;
1760
1761     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1762
1763     if (dwReserved)
1764     {
1765         ERR("dwReserved != 0\n");
1766         SetLastError(ERROR_INVALID_PARAMETER);
1767         return FALSE;
1768     }
1769
1770     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1771        return FALSE;
1772
1773     if (!URLCacheContainer_OpenIndex(pContainer))
1774         return FALSE;
1775
1776     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1777         return FALSE;
1778
1779     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1780     {
1781         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1782         TRACE("entry %s not found!\n", lpszUrlName);
1783         SetLastError(ERROR_FILE_NOT_FOUND);
1784         return FALSE;
1785     }
1786
1787     if (pEntry->dwSignature != URL_SIGNATURE)
1788     {
1789         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1790         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1791         SetLastError(ERROR_FILE_NOT_FOUND);
1792         return FALSE;
1793     }
1794
1795     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1796
1797     if (pUrlEntry->dwUseCount == 0)
1798     {
1799         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1800         return FALSE;
1801     }
1802     pUrlEntry->dwUseCount--;
1803     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1804
1805     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1806
1807     return TRUE;
1808 }
1809
1810 /***********************************************************************
1811  *           UnlockUrlCacheEntryFileW (WININET.@)
1812  *
1813  */
1814 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1815 {
1816     FIXME("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1817     return TRUE;
1818 }
1819
1820 /***********************************************************************
1821  *           CreateUrlCacheEntryA (WININET.@)
1822  *
1823  */
1824 BOOL WINAPI CreateUrlCacheEntryA(
1825     IN LPCSTR lpszUrlName,
1826     IN DWORD dwExpectedFileSize,
1827     IN LPCSTR lpszFileExtension,
1828     OUT LPSTR lpszFileName,
1829     IN DWORD dwReserved
1830 )
1831 {
1832     DWORD len;
1833     WCHAR *url_name;
1834     WCHAR *file_extension;
1835     WCHAR file_name[MAX_PATH];
1836     BOOL bSuccess = FALSE;
1837     DWORD dwError = 0;
1838
1839     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
1840         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1841     {
1842         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
1843         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
1844             (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1845         {
1846             MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
1847             if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
1848             {
1849                 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
1850                 {
1851                     bSuccess = TRUE;
1852                 }
1853                 else
1854                 {
1855                     dwError = GetLastError();
1856                 }
1857             }
1858             else
1859             {
1860                 dwError = GetLastError();
1861             }
1862             HeapFree(GetProcessHeap(), 0, file_extension);
1863         }
1864         else
1865         {
1866             dwError = GetLastError();
1867         }
1868         HeapFree(GetProcessHeap(), 0, url_name);
1869         if (!bSuccess)
1870             SetLastError(dwError);
1871     }
1872     return bSuccess;
1873 }
1874 /***********************************************************************
1875  *           CreateUrlCacheEntryW (WININET.@)
1876  *
1877  */
1878 BOOL WINAPI CreateUrlCacheEntryW(
1879     IN LPCWSTR lpszUrlName,
1880     IN DWORD dwExpectedFileSize,
1881     IN LPCWSTR lpszFileExtension,
1882     OUT LPWSTR lpszFileName,
1883     IN DWORD dwReserved
1884 )
1885 {
1886     URLCACHECONTAINER * pContainer;
1887     LPURLCACHE_HEADER pHeader;
1888     CHAR szFile[MAX_PATH];
1889     WCHAR szExtension[MAX_PATH];
1890     LPCWSTR lpszUrlPart;
1891     LPCWSTR lpszUrlEnd;
1892     LPCWSTR lpszFileNameExtension;
1893     LPWSTR lpszFileNameNoPath;
1894     int i;
1895     int countnoextension;
1896     BYTE CacheDir;
1897     LONG lBufferSize;
1898     BOOL bFound = FALSE;
1899     int count;
1900     static const WCHAR szWWW[] = {'w','w','w',0};
1901
1902     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
1903         debugstr_w(lpszUrlName),
1904         dwExpectedFileSize,
1905         debugstr_w(lpszFileExtension),
1906         lpszFileName,
1907         dwReserved);
1908
1909     if (dwReserved)
1910     {
1911         ERR("dwReserved != 0\n");
1912         SetLastError(ERROR_INVALID_PARAMETER);
1913         return FALSE;
1914     }
1915
1916     for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1917         ;
1918     
1919     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1920         lpszUrlEnd--;
1921
1922     for (lpszUrlPart = lpszUrlEnd; 
1923         (lpszUrlPart >= lpszUrlName); 
1924         lpszUrlPart--)
1925     {
1926         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1927         {
1928             bFound = TRUE;
1929             lpszUrlPart++;
1930             break;
1931         }
1932     }
1933     if (!lstrcmpW(lpszUrlPart, szWWW))
1934     {
1935         lpszUrlPart += lstrlenW(szWWW);
1936     }
1937
1938     count = lpszUrlEnd - lpszUrlPart;
1939
1940     if (bFound && (count < MAX_PATH))
1941     {
1942         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
1943         if (!len)
1944             return FALSE;
1945         szFile[len] = '\0';
1946         /* FIXME: get rid of illegal characters like \, / and : */
1947     }
1948     else
1949     {
1950         FIXME("need to generate a random filename\n");
1951     }
1952
1953     TRACE("File name: %s\n", debugstr_a(szFile));
1954
1955     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1956         return FALSE;
1957
1958     if (!URLCacheContainer_OpenIndex(pContainer))
1959         return FALSE;
1960
1961     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1962         return FALSE;
1963
1964     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1965
1966     lBufferSize = MAX_PATH * sizeof(WCHAR);
1967     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1968
1969     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1970
1971     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
1972         lpszFileNameNoPath >= lpszFileName; 
1973         --lpszFileNameNoPath)
1974     {
1975         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
1976             break;
1977     }
1978
1979     countnoextension = lstrlenW(lpszFileNameNoPath);
1980     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
1981     if (lpszFileNameExtension)
1982         countnoextension -= lstrlenW(lpszFileNameExtension);
1983     *szExtension = '\0';
1984
1985     if (lpszFileExtension)
1986     {
1987         szExtension[0] = '.';
1988         lstrcpyW(szExtension+1, lpszFileExtension);
1989     }
1990
1991     for (i = 0; i < 255; i++)
1992     {
1993         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
1994         HANDLE hFile;
1995         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
1996         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
1997         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1998         if (hFile != INVALID_HANDLE_VALUE)
1999         {
2000             CloseHandle(hFile);
2001             return TRUE;
2002         }
2003     }
2004
2005     return FALSE;
2006 }
2007
2008
2009 /***********************************************************************
2010  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2011  *
2012  *   The bug we are compensating for is that some drongo at Microsoft
2013  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2014  *   As a consequence, CommitUrlCacheEntryA has been effectively
2015  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2016  *   is still defined as LPCWSTR. The result (other than madness) is
2017  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2018  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2019  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2020  *   result will lose data for arbitrary binary data.
2021  *
2022  */
2023 static BOOL WINAPI CommitUrlCacheEntryInternal(
2024     IN LPCWSTR lpszUrlName,
2025     IN LPCWSTR lpszLocalFileName,
2026     IN FILETIME ExpireTime,
2027     IN FILETIME LastModifiedTime,
2028     IN DWORD CacheEntryType,
2029     IN LPBYTE lpHeaderInfo,
2030     IN DWORD dwHeaderSize,
2031     IN LPCWSTR lpszFileExtension,
2032     IN LPCWSTR lpszOriginalUrl
2033     )
2034 {
2035     URLCACHECONTAINER * pContainer;
2036     LPURLCACHE_HEADER pHeader;
2037     CACHEFILE_ENTRY * pEntry;
2038     URL_CACHEFILE_ENTRY * pUrlEntry;
2039     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2040     DWORD dwOffsetLocalFileName = 0;
2041     DWORD dwOffsetHeader = 0;
2042     DWORD dwFileSizeLow = 0;
2043     DWORD dwFileSizeHigh = 0;
2044     BYTE cDirectory = 0;
2045     char achFile[MAX_PATH];
2046     char achUrl[MAX_PATH];
2047     char *pchLocalFileName = 0;
2048
2049     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2050         debugstr_w(lpszUrlName),
2051         debugstr_w(lpszLocalFileName),
2052         CacheEntryType,
2053         lpHeaderInfo,
2054         dwHeaderSize,
2055         debugstr_w(lpszFileExtension),
2056         debugstr_w(lpszOriginalUrl));
2057
2058     if (lpszOriginalUrl)
2059         WARN(": lpszOriginalUrl ignored\n");
2060  
2061     if (lpszLocalFileName)
2062     {
2063         HANDLE hFile;
2064
2065         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2066         if (hFile == INVALID_HANDLE_VALUE)
2067         {
2068             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2069             return FALSE;
2070         }
2071
2072         /* Get file size */
2073         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2074         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2075         {
2076             ERR("couldn't get file size (error is %d)\n", GetLastError());
2077             CloseHandle(hFile);
2078             return FALSE;
2079         }
2080
2081         CloseHandle(hFile);
2082     }
2083
2084     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2085         return FALSE;
2086
2087     if (!URLCacheContainer_OpenIndex(pContainer))
2088         return FALSE;
2089
2090     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2091         return FALSE;
2092
2093     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
2094
2095     if (URLCache_FindEntryInHash(pHeader, achUrl, &pEntry))
2096     {
2097         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2098         FIXME("entry already in cache - don't know what to do!\n");
2099 /*
2100  *        SetLastError(ERROR_FILE_NOT_FOUND);
2101  *        return FALSE;
2102  */
2103         return TRUE;
2104     }
2105
2106     if (lpszLocalFileName)
2107     {
2108         BOOL bFound = FALSE;
2109
2110         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2111         {
2112             URLCacheContainer_UnlockIndex(pContainer, pHeader);
2113             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2114             SetLastError(ERROR_INVALID_PARAMETER);
2115             return FALSE;
2116         }
2117
2118         /* skip container path prefix */
2119         lpszLocalFileName += lstrlenW(pContainer->path);
2120
2121         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2122         pchLocalFileName = achFile;
2123
2124         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2125         {
2126             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2127             {
2128                 bFound = TRUE;
2129                 break;
2130             }
2131         }
2132
2133         if (!bFound)
2134         {
2135             URLCacheContainer_UnlockIndex(pContainer, pHeader);
2136             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2137             SetLastError(ERROR_INVALID_PARAMETER);
2138             return FALSE;
2139         }
2140
2141         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2142     }
2143
2144     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
2145     if (lpszLocalFileName)
2146     {
2147         dwOffsetLocalFileName = dwBytesNeeded;
2148         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2149     }
2150     if (lpHeaderInfo)
2151     {
2152         dwOffsetHeader = dwBytesNeeded;
2153         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2154     }
2155
2156     /* round up to next block */
2157     if (dwBytesNeeded % BLOCKSIZE)
2158     {
2159         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2160         dwBytesNeeded += BLOCKSIZE;
2161     }
2162
2163     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2164     {
2165         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2166         ERR("no free entries\n");
2167         SetLastError(ERROR_DISK_FULL);
2168         return FALSE;
2169     }
2170
2171     /* FindFirstFreeEntry fills in blocks used */
2172     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2173     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2174     pUrlEntry->CacheDir = cDirectory;
2175     pUrlEntry->CacheEntryType = CacheEntryType;
2176     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2177     pUrlEntry->dwExemptDelta = 0;
2178     pUrlEntry->dwHitRate = 0;
2179     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2180     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2181     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2182     pUrlEntry->dwSizeHigh = 0;
2183     pUrlEntry->dwSizeLow = dwFileSizeLow;
2184     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2185     pUrlEntry->dwUseCount = 0;
2186     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2187     pUrlEntry->LastModifiedTime = LastModifiedTime;
2188     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2189     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2190     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2191     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2192
2193     /*** Unknowns ***/
2194     pUrlEntry->dwUnknown1 = 0;
2195     pUrlEntry->dwUnknown2 = 0;
2196     pUrlEntry->dwUnknown3 = 0x60;
2197     pUrlEntry->Unknown4 = 0;
2198     pUrlEntry->wUnknown5 = 0x1010;
2199     pUrlEntry->dwUnknown6 = 0;
2200     pUrlEntry->dwUnknown7 = 0;
2201     pUrlEntry->dwUnknown8 = 0;
2202
2203
2204     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, achUrl);
2205     if (dwOffsetLocalFileName)
2206         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2207     if (dwOffsetHeader)
2208         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2209
2210     if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2211     {
2212         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2213         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2214         return FALSE;
2215     }
2216
2217     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2218
2219     return TRUE;
2220 }
2221
2222 /***********************************************************************
2223  *           CommitUrlCacheEntryA (WININET.@)
2224  *
2225  */
2226 BOOL WINAPI CommitUrlCacheEntryA(
2227     IN LPCSTR lpszUrlName,
2228     IN LPCSTR lpszLocalFileName,
2229     IN FILETIME ExpireTime,
2230     IN FILETIME LastModifiedTime,
2231     IN DWORD CacheEntryType,
2232     IN LPBYTE lpHeaderInfo,
2233     IN DWORD dwHeaderSize,
2234     IN LPCSTR lpszFileExtension,
2235     IN LPCSTR lpszOriginalUrl
2236     )
2237 {
2238     DWORD len;
2239     WCHAR *url_name;
2240     WCHAR *local_file_name;
2241     WCHAR *original_url = NULL;
2242     BOOL bSuccess = FALSE;
2243     DWORD dwError = 0;
2244
2245     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2246         debugstr_a(lpszUrlName),
2247         debugstr_a(lpszLocalFileName),
2248         CacheEntryType,
2249         lpHeaderInfo,
2250         dwHeaderSize,
2251         debugstr_a(lpszFileExtension),
2252         debugstr_a(lpszOriginalUrl));
2253
2254     if (lpszFileExtension != 0)
2255     {
2256         SetLastError(ERROR_INVALID_PARAMETER);
2257         return FALSE;
2258     }
2259     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2260         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2261     {
2262         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2263         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2264             (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2265         {
2266             MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2267             if (!lpszOriginalUrl ||
2268                 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2269                   (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2270             {
2271                 if (original_url)
2272                     MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2273                 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2274                                         CacheEntryType, lpHeaderInfo, dwHeaderSize,
2275                                         NULL, original_url))
2276                 {
2277                     bSuccess = TRUE;
2278                 }
2279                 else
2280                 {
2281                 dwError = GetLastError();
2282                 }
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, ..., ..., %x, %p, %d, %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%08x)\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%08x)\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%08x, 0x%08x) stub\n", d1, d2);
2551     return TRUE;
2552 }
2553
2554 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2555 {
2556     FIXME("(0x%08x, 0x%08x) 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%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) 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%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) 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%08x) 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%08x) 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%08x, 0x%08x, 0x%08x%08x, %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%08x, 0x%08x, 0x%08x%08x, %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%08x, 0x%08x, %p, 0x%08x, %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%08x, %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%08x%08x, 0x%08x, %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%08x, 0x%08x%08x, %p, 0x%08x, %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%08x, 0x%08x%08x, %p, 0x%08x, %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, %x)\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, %x)\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%08x%08x, 0x%08x, 0x%08x, %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%08x%08x, 0x%08x, 0x%08x, %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%08x%08x, 0x%08x, 0x%08x, %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%08x%08x, 0x%08x, 0x%08x, %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%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2862     return TRUE;
2863 }
2864
2865 BOOL WINAPI SetUrlCacheConfigInfoW( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2866 {
2867     FIXME("(%p, 0x%08x) 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 }