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