wininet: Fix the return value check of SHGetSpecialFolderPathW in URLCacheContainers_...
[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 (!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 = (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 == (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 | (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 = (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    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
1978     
1979     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1980         lpszUrlEnd--;
1981
1982     for (lpszUrlPart = lpszUrlEnd; 
1983         (lpszUrlPart >= lpszUrlName); 
1984         lpszUrlPart--)
1985     {
1986         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1987         {
1988             bFound = TRUE;
1989             lpszUrlPart++;
1990             break;
1991         }
1992     }
1993     if (!lstrcmpW(lpszUrlPart, szWWW))
1994     {
1995         lpszUrlPart += lstrlenW(szWWW);
1996     }
1997
1998     count = lpszUrlEnd - lpszUrlPart;
1999
2000     if (bFound && (count < MAX_PATH))
2001     {
2002         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2003         if (!len)
2004             return FALSE;
2005         szFile[len] = '\0';
2006         /* FIXME: get rid of illegal characters like \, / and : */
2007     }
2008     else
2009     {
2010         FIXME("need to generate a random filename\n");
2011     }
2012
2013     TRACE("File name: %s\n", debugstr_a(szFile));
2014
2015     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2016         return FALSE;
2017
2018     if (!URLCacheContainer_OpenIndex(pContainer))
2019         return FALSE;
2020
2021     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2022         return FALSE;
2023
2024     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2025
2026     lBufferSize = MAX_PATH * sizeof(WCHAR);
2027     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2028
2029     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2030
2031     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2032         lpszFileNameNoPath >= lpszFileName; 
2033         --lpszFileNameNoPath)
2034     {
2035         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2036             break;
2037     }
2038
2039     countnoextension = lstrlenW(lpszFileNameNoPath);
2040     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2041     if (lpszFileNameExtension)
2042         countnoextension -= lstrlenW(lpszFileNameExtension);
2043     *szExtension = '\0';
2044
2045     if (lpszFileExtension)
2046     {
2047         szExtension[0] = '.';
2048         lstrcpyW(szExtension+1, lpszFileExtension);
2049     }
2050
2051     for (i = 0; i < 255; i++)
2052     {
2053         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2054         HANDLE hFile;
2055         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2056         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2057         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2058         if (hFile != INVALID_HANDLE_VALUE)
2059         {
2060             CloseHandle(hFile);
2061             return TRUE;
2062         }
2063     }
2064
2065     return FALSE;
2066 }
2067
2068
2069 /***********************************************************************
2070  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2071  *
2072  *   The bug we are compensating for is that some drongo at Microsoft
2073  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2074  *   As a consequence, CommitUrlCacheEntryA has been effectively
2075  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2076  *   is still defined as LPCWSTR. The result (other than madness) is
2077  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2078  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2079  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2080  *   result will lose data for arbitrary binary data.
2081  *
2082  */
2083 static BOOL WINAPI CommitUrlCacheEntryInternal(
2084     IN LPCWSTR lpszUrlName,
2085     IN LPCWSTR lpszLocalFileName,
2086     IN FILETIME ExpireTime,
2087     IN FILETIME LastModifiedTime,
2088     IN DWORD CacheEntryType,
2089     IN LPBYTE lpHeaderInfo,
2090     IN DWORD dwHeaderSize,
2091     IN LPCWSTR lpszFileExtension,
2092     IN LPCWSTR lpszOriginalUrl
2093     )
2094 {
2095     URLCACHECONTAINER * pContainer;
2096     LPURLCACHE_HEADER pHeader;
2097     struct _HASH_ENTRY * pHashEntry;
2098     CACHEFILE_ENTRY * pEntry;
2099     URL_CACHEFILE_ENTRY * pUrlEntry;
2100     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2101     DWORD dwOffsetLocalFileName = 0;
2102     DWORD dwOffsetHeader = 0;
2103     DWORD dwFileSizeLow = 0;
2104     DWORD dwFileSizeHigh = 0;
2105     BYTE cDirectory = 0;
2106     char achFile[MAX_PATH];
2107     char achUrl[MAX_PATH];
2108     char *pchLocalFileName = 0;
2109
2110     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2111         debugstr_w(lpszUrlName),
2112         debugstr_w(lpszLocalFileName),
2113         CacheEntryType,
2114         lpHeaderInfo,
2115         dwHeaderSize,
2116         debugstr_w(lpszFileExtension),
2117         debugstr_w(lpszOriginalUrl));
2118
2119     if (lpszOriginalUrl)
2120         WARN(": lpszOriginalUrl ignored\n");
2121  
2122     if (lpszLocalFileName)
2123     {
2124         HANDLE hFile;
2125
2126         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2127         if (hFile == INVALID_HANDLE_VALUE)
2128         {
2129             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2130             return FALSE;
2131         }
2132
2133         /* Get file size */
2134         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2135         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2136         {
2137             ERR("couldn't get file size (error is %d)\n", GetLastError());
2138             CloseHandle(hFile);
2139             return FALSE;
2140         }
2141
2142         CloseHandle(hFile);
2143     }
2144
2145     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2146         return FALSE;
2147
2148     if (!URLCacheContainer_OpenIndex(pContainer))
2149         return FALSE;
2150
2151     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2152         return FALSE;
2153
2154     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
2155
2156     if (URLCache_FindHash(pHeader, achUrl, &pHashEntry))
2157     {
2158         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2159         FIXME("entry already in cache - don't know what to do!\n");
2160 /*
2161  *        SetLastError(ERROR_FILE_NOT_FOUND);
2162  *        return FALSE;
2163  */
2164         return TRUE;
2165     }
2166
2167     if (lpszLocalFileName)
2168     {
2169         BOOL bFound = FALSE;
2170
2171         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2172         {
2173             URLCacheContainer_UnlockIndex(pContainer, pHeader);
2174             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2175             SetLastError(ERROR_INVALID_PARAMETER);
2176             return FALSE;
2177         }
2178
2179         /* skip container path prefix */
2180         lpszLocalFileName += lstrlenW(pContainer->path);
2181
2182         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2183         pchLocalFileName = achFile;
2184
2185         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2186         {
2187             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2188             {
2189                 bFound = TRUE;
2190                 break;
2191             }
2192         }
2193
2194         if (!bFound)
2195         {
2196             URLCacheContainer_UnlockIndex(pContainer, pHeader);
2197             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2198             SetLastError(ERROR_INVALID_PARAMETER);
2199             return FALSE;
2200         }
2201
2202         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2203     }
2204
2205     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
2206     if (lpszLocalFileName)
2207     {
2208         dwOffsetLocalFileName = dwBytesNeeded;
2209         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2210     }
2211     if (lpHeaderInfo)
2212     {
2213         dwOffsetHeader = dwBytesNeeded;
2214         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2215     }
2216
2217     /* round up to next block */
2218     if (dwBytesNeeded % BLOCKSIZE)
2219     {
2220         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2221         dwBytesNeeded += BLOCKSIZE;
2222     }
2223
2224     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2225     {
2226         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2227         ERR("no free entries\n");
2228         SetLastError(ERROR_DISK_FULL);
2229         return FALSE;
2230     }
2231
2232     /* FindFirstFreeEntry fills in blocks used */
2233     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2234     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2235     pUrlEntry->CacheDir = cDirectory;
2236     pUrlEntry->CacheEntryType = CacheEntryType;
2237     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2238     pUrlEntry->dwExemptDelta = 0;
2239     pUrlEntry->dwHitRate = 0;
2240     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2241     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2242     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2243     pUrlEntry->dwSizeHigh = 0;
2244     pUrlEntry->dwSizeLow = dwFileSizeLow;
2245     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2246     pUrlEntry->dwUseCount = 0;
2247     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2248     pUrlEntry->LastModifiedTime = LastModifiedTime;
2249     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2250     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2251     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2252     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2253
2254     /*** Unknowns ***/
2255     pUrlEntry->dwUnknown1 = 0;
2256     pUrlEntry->dwUnknown2 = 0;
2257     pUrlEntry->dwUnknown3 = 0x60;
2258     pUrlEntry->Unknown4 = 0;
2259     pUrlEntry->wUnknown5 = 0x1010;
2260     pUrlEntry->dwUnknown6 = 0;
2261     pUrlEntry->dwUnknown7 = 0;
2262     pUrlEntry->dwUnknown8 = 0;
2263
2264
2265     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, achUrl);
2266     if (dwOffsetLocalFileName)
2267         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2268     if (dwOffsetHeader)
2269         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2270
2271     if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2272     {
2273         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2274         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2275         return FALSE;
2276     }
2277
2278     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2279
2280     return TRUE;
2281 }
2282
2283 /***********************************************************************
2284  *           CommitUrlCacheEntryA (WININET.@)
2285  *
2286  */
2287 BOOL WINAPI CommitUrlCacheEntryA(
2288     IN LPCSTR lpszUrlName,
2289     IN LPCSTR lpszLocalFileName,
2290     IN FILETIME ExpireTime,
2291     IN FILETIME LastModifiedTime,
2292     IN DWORD CacheEntryType,
2293     IN LPBYTE lpHeaderInfo,
2294     IN DWORD dwHeaderSize,
2295     IN LPCSTR lpszFileExtension,
2296     IN LPCSTR lpszOriginalUrl
2297     )
2298 {
2299     DWORD len;
2300     WCHAR *url_name;
2301     WCHAR *local_file_name;
2302     WCHAR *original_url = NULL;
2303     BOOL bSuccess = FALSE;
2304     DWORD dwError = 0;
2305
2306     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2307         debugstr_a(lpszUrlName),
2308         debugstr_a(lpszLocalFileName),
2309         CacheEntryType,
2310         lpHeaderInfo,
2311         dwHeaderSize,
2312         debugstr_a(lpszFileExtension),
2313         debugstr_a(lpszOriginalUrl));
2314
2315     if (lpszFileExtension != 0)
2316     {
2317         SetLastError(ERROR_INVALID_PARAMETER);
2318         return FALSE;
2319     }
2320     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2321         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2322     {
2323         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2324         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2325             (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2326         {
2327             MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2328             if (!lpszOriginalUrl ||
2329                 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2330                   (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2331             {
2332                 if (original_url)
2333                     MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2334                 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2335                                         CacheEntryType, lpHeaderInfo, dwHeaderSize,
2336                                         NULL, original_url))
2337                 {
2338                     bSuccess = TRUE;
2339                 }
2340                 else
2341                 {
2342                 dwError = GetLastError();
2343                 }
2344                 HeapFree(GetProcessHeap(), 0, original_url);
2345             }
2346                 else
2347             {
2348                 dwError = GetLastError();
2349             }
2350             HeapFree(GetProcessHeap(), 0, local_file_name);
2351         }
2352         else
2353         {
2354             dwError = GetLastError();
2355         }
2356         HeapFree(GetProcessHeap(), 0, url_name);
2357         if (!bSuccess)
2358             SetLastError(dwError);
2359     }
2360     return bSuccess;
2361 }
2362
2363 /***********************************************************************
2364  *           CommitUrlCacheEntryW (WININET.@)
2365  *
2366  */
2367 BOOL WINAPI CommitUrlCacheEntryW(
2368     IN LPCWSTR lpszUrlName,
2369     IN LPCWSTR lpszLocalFileName,
2370     IN FILETIME ExpireTime,
2371     IN FILETIME LastModifiedTime,
2372     IN DWORD CacheEntryType,
2373     IN LPWSTR lpHeaderInfo,
2374     IN DWORD dwHeaderSize,
2375     IN LPCWSTR lpszFileExtension,
2376     IN LPCWSTR lpszOriginalUrl
2377     )
2378 {
2379     DWORD dwError = 0;
2380     BOOL bSuccess = FALSE;
2381     DWORD len = 0;
2382     CHAR *header_info = NULL;
2383
2384     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2385         debugstr_w(lpszUrlName),
2386         debugstr_w(lpszLocalFileName),
2387         CacheEntryType,
2388         lpHeaderInfo,
2389         dwHeaderSize,
2390         debugstr_w(lpszFileExtension),
2391         debugstr_w(lpszOriginalUrl));
2392
2393     if (!lpHeaderInfo ||
2394         ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2395          (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2396     {
2397         if (header_info)
2398             WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2399         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2400                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2401         {
2402                 bSuccess = TRUE;
2403         }
2404         else
2405         {
2406                 dwError = GetLastError();
2407         }
2408         if (header_info)
2409         {
2410             HeapFree(GetProcessHeap(), 0, header_info);
2411             if (!bSuccess)
2412                 SetLastError(dwError);
2413         }
2414     }
2415     return bSuccess;
2416 }
2417
2418 /***********************************************************************
2419  *           ReadUrlCacheEntryStream (WININET.@)
2420  *
2421  */
2422 BOOL WINAPI ReadUrlCacheEntryStream(
2423     IN HANDLE hUrlCacheStream,
2424     IN  DWORD dwLocation,
2425     IN OUT LPVOID lpBuffer,
2426     IN OUT LPDWORD lpdwLen,
2427     IN DWORD dwReserved
2428     )
2429 {
2430     /* Get handle to file from 'stream' */
2431     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2432
2433     if (dwReserved != 0)
2434     {
2435         ERR("dwReserved != 0\n");
2436         SetLastError(ERROR_INVALID_PARAMETER);
2437         return FALSE;
2438     }
2439
2440     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2441     {
2442         SetLastError(ERROR_INVALID_HANDLE);
2443         return FALSE;
2444     }
2445
2446     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2447         return FALSE;
2448     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2449 }
2450
2451 /***********************************************************************
2452  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2453  *
2454  */
2455 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2456     IN LPCSTR lpszUrlName,
2457     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2458     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2459     IN BOOL fRandomRead,
2460     IN DWORD dwReserved
2461     )
2462 {
2463     /* NOTE: this is not the same as the way that the native
2464      * version allocates 'stream' handles. I did it this way
2465      * as it is much easier and no applications should depend
2466      * on this behaviour. (Native version appears to allocate
2467      * indices into a table)
2468      */
2469     STREAM_HANDLE * pStream;
2470     HANDLE hFile;
2471
2472     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2473            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2474
2475     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2476         lpCacheEntryInfo,
2477         lpdwCacheEntryInfoBufferSize,
2478         dwReserved))
2479     {
2480         return NULL;
2481     }
2482
2483     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2484         GENERIC_READ,
2485         FILE_SHARE_READ,
2486         NULL,
2487         OPEN_EXISTING,
2488         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2489         NULL);
2490     if (hFile == INVALID_HANDLE_VALUE)
2491         return FALSE;
2492     
2493     /* allocate handle storage space */
2494     pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2495     if (!pStream)
2496     {
2497         CloseHandle(hFile);
2498         SetLastError(ERROR_OUTOFMEMORY);
2499         return FALSE;
2500     }
2501
2502     pStream->hFile = hFile;
2503     strcpy(pStream->lpszUrl, lpszUrlName);
2504     return (HANDLE)pStream;
2505 }
2506
2507 /***********************************************************************
2508  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2509  *
2510  */
2511 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2512     IN LPCWSTR lpszUrlName,
2513     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2514     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2515     IN BOOL fRandomRead,
2516     IN DWORD dwReserved
2517     )
2518 {
2519     FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2520            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2521     return NULL;
2522 }
2523
2524 /***********************************************************************
2525  *           UnlockUrlCacheEntryStream (WININET.@)
2526  *
2527  */
2528 BOOL WINAPI UnlockUrlCacheEntryStream(
2529     IN HANDLE hUrlCacheStream,
2530     IN DWORD dwReserved
2531 )
2532 {
2533     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2534
2535     if (dwReserved != 0)
2536     {
2537         ERR("dwReserved != 0\n");
2538         SetLastError(ERROR_INVALID_PARAMETER);
2539         return FALSE;
2540     }
2541
2542     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2543     {
2544         SetLastError(ERROR_INVALID_HANDLE);
2545         return FALSE;
2546     }
2547
2548     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2549         return FALSE;
2550
2551     /* close file handle */
2552     CloseHandle(pStream->hFile);
2553
2554     /* free allocated space */
2555     HeapFree(GetProcessHeap(), 0, pStream);
2556
2557     return TRUE;
2558 }
2559
2560
2561 /***********************************************************************
2562  *           DeleteUrlCacheEntryA (WININET.@)
2563  *
2564  */
2565 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2566 {
2567     URLCACHECONTAINER * pContainer;
2568     LPURLCACHE_HEADER pHeader;
2569     struct _HASH_ENTRY * pHashEntry;
2570     CACHEFILE_ENTRY * pEntry;
2571
2572     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2573
2574     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2575         return FALSE;
2576
2577     if (!URLCacheContainer_OpenIndex(pContainer))
2578         return FALSE;
2579
2580     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2581         return FALSE;
2582
2583     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2584     {
2585         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2586         TRACE("entry %s not found!\n", lpszUrlName);
2587         SetLastError(ERROR_FILE_NOT_FOUND);
2588         return FALSE;
2589     }
2590
2591     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2592     URLCache_DeleteEntry(pHeader, pEntry);
2593
2594     URLCache_DeleteEntryFromHash(pHashEntry);
2595
2596     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2597
2598     return TRUE;
2599 }
2600
2601 /***********************************************************************
2602  *           DeleteUrlCacheEntryW (WININET.@)
2603  *
2604  */
2605 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2606 {
2607     URLCACHECONTAINER * pContainer;
2608     LPURLCACHE_HEADER pHeader;
2609     struct _HASH_ENTRY * pHashEntry;
2610     CACHEFILE_ENTRY * pEntry;
2611     LPSTR urlA;
2612     int url_len;
2613
2614     TRACE("(%s)\n", debugstr_w(lpszUrlName));
2615
2616     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2617     urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2618     if (!urlA)
2619     {
2620         SetLastError(ERROR_OUTOFMEMORY);
2621         return FALSE;
2622     }
2623     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2624
2625     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2626     {
2627         HeapFree(GetProcessHeap(), 0, urlA);
2628         return FALSE;
2629     }
2630     if (!URLCacheContainer_OpenIndex(pContainer))
2631     {
2632         HeapFree(GetProcessHeap(), 0, urlA);
2633         return FALSE;
2634     }
2635     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2636     {
2637         HeapFree(GetProcessHeap(), 0, urlA);
2638         return FALSE;
2639     }
2640
2641     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2642     {
2643         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2644         TRACE("entry %s not found!\n", debugstr_a(urlA));
2645         HeapFree(GetProcessHeap(), 0, urlA);
2646         SetLastError(ERROR_FILE_NOT_FOUND);
2647         return FALSE;
2648     }
2649
2650     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2651     URLCache_DeleteEntry(pHeader, pEntry);
2652
2653     URLCache_DeleteEntryFromHash(pHashEntry);
2654
2655     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2656
2657     HeapFree(GetProcessHeap(), 0, urlA);
2658     return TRUE;
2659 }
2660
2661 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2662 {
2663     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2664     return TRUE;
2665 }
2666
2667 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2668 {
2669     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2670     return TRUE;
2671 }
2672
2673 /***********************************************************************
2674  *           CreateCacheContainerA (WININET.@)
2675  */
2676 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2677                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2678 {
2679     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2680           d1, d2, d3, d4, d5, d6, d7, d8);
2681     return TRUE;
2682 }
2683
2684 /***********************************************************************
2685  *           CreateCacheContainerW (WININET.@)
2686  */
2687 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2688                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2689 {
2690     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2691           d1, d2, d3, d4, d5, d6, d7, d8);
2692     return TRUE;
2693 }
2694
2695 /***********************************************************************
2696  *           FindCloseUrlCache (WININET.@)
2697  */
2698 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2699 {
2700     FIXME("(%p) stub\n", hEnumHandle);
2701     return TRUE;
2702 }
2703
2704 /***********************************************************************
2705  *           FindFirstUrlCacheContainerA (WININET.@)
2706  */
2707 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2708 {
2709     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2710     return NULL;
2711 }
2712
2713 /***********************************************************************
2714  *           FindFirstUrlCacheContainerW (WININET.@)
2715  */
2716 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2717 {
2718     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2719     return NULL;
2720 }
2721
2722 /***********************************************************************
2723  *           FindNextUrlCacheContainerA (WININET.@)
2724  */
2725 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2726 {
2727     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2728     return FALSE;
2729 }
2730
2731 /***********************************************************************
2732  *           FindNextUrlCacheContainerW (WININET.@)
2733  */
2734 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2735 {
2736     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2737     return FALSE;
2738 }
2739
2740 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2741   LPCSTR lpszUrlSearchPattern,
2742   DWORD dwFlags,
2743   DWORD dwFilter,
2744   GROUPID GroupId,
2745   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2746   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2747   LPVOID lpReserved,
2748   LPDWORD pcbReserved2,
2749   LPVOID lpReserved3
2750 )
2751 {
2752     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2753           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2754           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2755     SetLastError(ERROR_FILE_NOT_FOUND);
2756     return NULL;
2757 }
2758
2759 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2760   LPCWSTR lpszUrlSearchPattern,
2761   DWORD dwFlags,
2762   DWORD dwFilter,
2763   GROUPID GroupId,
2764   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2765   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2766   LPVOID lpReserved,
2767   LPDWORD pcbReserved2,
2768   LPVOID lpReserved3
2769 )
2770 {
2771     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2772           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2773           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2774     SetLastError(ERROR_FILE_NOT_FOUND);
2775     return NULL;
2776 }
2777
2778 /***********************************************************************
2779  *           FindFirstUrlCacheEntryA (WININET.@)
2780  *
2781  */
2782 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2783  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2784 {
2785   FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2786   SetLastError(ERROR_FILE_NOT_FOUND);
2787   return 0;
2788 }
2789
2790 /***********************************************************************
2791  *           FindFirstUrlCacheEntryW (WININET.@)
2792  *
2793  */
2794 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2795  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2796 {
2797   FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2798   SetLastError(ERROR_FILE_NOT_FOUND);
2799   return 0;
2800 }
2801
2802 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2803                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2804 {
2805     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2806           dwSearchCondition, lpGroupId, lpReserved);
2807     return NULL;
2808 }
2809
2810 BOOL WINAPI FindNextUrlCacheEntryA(
2811   HANDLE hEnumHandle,
2812   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2813   LPDWORD lpdwNextCacheEntryInfoBufferSize
2814 )
2815 {
2816     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2817     return FALSE;
2818 }
2819
2820 BOOL WINAPI FindNextUrlCacheEntryW(
2821   HANDLE hEnumHandle,
2822   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2823   LPDWORD lpdwNextCacheEntryInfoBufferSize
2824 )
2825 {
2826     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2827     return FALSE;
2828 }
2829
2830 BOOL WINAPI FindNextUrlCacheEntryExA(
2831   HANDLE hEnumHandle,
2832   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2833   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2834   LPVOID lpReserved,
2835   LPDWORD pcbReserved2,
2836   LPVOID lpReserved3
2837 )
2838 {
2839     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2840           lpReserved, pcbReserved2, lpReserved3);
2841     return FALSE;
2842 }
2843
2844 BOOL WINAPI FindNextUrlCacheEntryExW(
2845   HANDLE hEnumHandle,
2846   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2847   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2848   LPVOID lpReserved,
2849   LPDWORD pcbReserved2,
2850   LPVOID lpReserved3
2851 )
2852 {
2853     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2854           lpReserved, pcbReserved2, lpReserved3);
2855     return FALSE;
2856 }
2857
2858 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2859 {
2860     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2861     return FALSE;
2862 }
2863
2864 /***********************************************************************
2865  *           CreateUrlCacheGroup (WININET.@)
2866  *
2867  */
2868 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2869 {
2870   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
2871   return FALSE;
2872 }
2873
2874 /***********************************************************************
2875  *           DeleteUrlCacheGroup (WININET.@)
2876  *
2877  */
2878 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2879 {
2880     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
2881           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2882     return FALSE;
2883 }
2884
2885 /***********************************************************************
2886  *           SetUrlCacheEntryGroupA (WININET.@)
2887  *
2888  */
2889 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2890   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2891   LPVOID lpReserved)
2892 {
2893     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2894           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2895           pbGroupAttributes, cbGroupAttributes, lpReserved);
2896     SetLastError(ERROR_FILE_NOT_FOUND);
2897     return FALSE;
2898 }
2899
2900 /***********************************************************************
2901  *           SetUrlCacheEntryGroupW (WININET.@)
2902  *
2903  */
2904 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2905   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2906   LPVOID lpReserved)
2907 {
2908     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2909           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2910           pbGroupAttributes, cbGroupAttributes, lpReserved);
2911     SetLastError(ERROR_FILE_NOT_FOUND);
2912     return FALSE;
2913 }
2914
2915 /***********************************************************************
2916  *           GetUrlCacheConfigInfoW (WININET.@)
2917  */
2918 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
2919 {
2920     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2921     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2922     return FALSE;
2923 }
2924
2925 /***********************************************************************
2926  *           GetUrlCacheConfigInfoA (WININET.@)
2927  *
2928  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2929  */
2930 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
2931 {
2932     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2933     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2934     return FALSE;
2935 }
2936
2937 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2938                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2939                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2940 {
2941     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2942           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2943           lpdwGroupInfo, lpReserved);
2944     return FALSE;
2945 }
2946
2947 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2948                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2949                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2950 {
2951     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2952           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2953           lpdwGroupInfo, lpReserved);
2954     return FALSE;
2955 }
2956
2957 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2958                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2959 {
2960     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2961           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2962     return TRUE;
2963 }
2964
2965 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2966                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2967 {
2968     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2969           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2970     return TRUE;
2971 }
2972
2973 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
2974 {
2975     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2976     return TRUE;
2977 }
2978
2979 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
2980 {
2981     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2982     return TRUE;
2983 }
2984
2985 /***********************************************************************
2986  *           DeleteIE3Cache (WININET.@)
2987  *
2988  * Deletes the files used by the IE3 URL caching system.
2989  *
2990  * PARAMS
2991  *   hWnd        [I] A dummy window.
2992  *   hInst       [I] Instance of process calling the function.
2993  *   lpszCmdLine [I] Options used by function.
2994  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
2995  */
2996 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2997 {
2998     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
2999     return 0;
3000 }
3001
3002 /***********************************************************************
3003  *           IsUrlCacheEntryExpiredA (WININET.@)
3004  *
3005  * PARAMS
3006  *   url             [I] Url
3007  *   dwFlags         [I] Unknown
3008  *   pftLastModified [O] Last modified time
3009  */
3010 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3011 {
3012     LPURLCACHE_HEADER pHeader;
3013     struct _HASH_ENTRY * pHashEntry;
3014     CACHEFILE_ENTRY * pEntry;
3015     URL_CACHEFILE_ENTRY * pUrlEntry;
3016     URLCACHECONTAINER * pContainer;
3017
3018     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3019
3020     if (!URLCacheContainers_FindContainerA(url, &pContainer))
3021         return FALSE;
3022
3023     if (!URLCacheContainer_OpenIndex(pContainer))
3024         return FALSE;
3025
3026     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3027         return FALSE;
3028
3029     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3030     {
3031         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3032         TRACE("entry %s not found!\n", url);
3033         SetLastError(ERROR_FILE_NOT_FOUND);
3034         return FALSE;
3035     }
3036
3037     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3038     if (pEntry->dwSignature != URL_SIGNATURE)
3039     {
3040         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3041         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3042         SetLastError(ERROR_FILE_NOT_FOUND);
3043         return FALSE;
3044     }
3045
3046     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3047
3048     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3049
3050     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3051
3052     return TRUE;
3053 }
3054
3055 /***********************************************************************
3056  *           IsUrlCacheEntryExpiredW (WININET.@)
3057  *
3058  * PARAMS
3059  *   url             [I] Url
3060  *   dwFlags         [I] Unknown
3061  *   pftLastModified [O] Last modified time
3062  */
3063 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3064 {
3065     LPURLCACHE_HEADER pHeader;
3066     struct _HASH_ENTRY * pHashEntry;
3067     CACHEFILE_ENTRY * pEntry;
3068     URL_CACHEFILE_ENTRY * pUrlEntry;
3069     URLCACHECONTAINER * pContainer;
3070
3071     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3072
3073     if (!URLCacheContainers_FindContainerW(url, &pContainer))
3074         return FALSE;
3075
3076     if (!URLCacheContainer_OpenIndex(pContainer))
3077         return FALSE;
3078
3079     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3080         return FALSE;
3081
3082     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3083     {
3084         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3085         TRACE("entry %s not found!\n", debugstr_w(url));
3086         SetLastError(ERROR_FILE_NOT_FOUND);
3087         return FALSE;
3088     }
3089
3090     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3091     if (pEntry->dwSignature != URL_SIGNATURE)
3092     {
3093         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3094         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3095         SetLastError(ERROR_FILE_NOT_FOUND);
3096         return FALSE;
3097     }
3098
3099     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3100
3101     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3102
3103     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3104
3105     return TRUE;
3106 }