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