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