wininet: Don't crash in GetUrlCacheEntryInfoA/W if lpdwCacheEntryInfoBufferSize is...
[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 dwOffsetFileExtension; /* offset of start of file extension from start of entry */
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                                 /* The following is OK because we generated an
379                                  * 8 character directory name made from characters
380                                  * [A-Z0-9], which are equivalent for all code
381                                  * pages and for UTF-16
382                                  */
383                                 for (k = 0; k < 8; ++k)
384                                     pHeader->directory_data[i].filename[k] = pwchDir[k];
385                                 break;
386                             }
387                             else if (j >= 255)
388                             {
389                                 /* Give up. The most likely cause of this
390                                  * is a full disk, but whatever the cause
391                                  * is, it should be more than apparent that
392                                  * we won't succeed.
393                                  */
394                                 dwError = GetLastError();
395                                 break;
396                             }
397                         }
398                     }
399                 
400                     UnmapViewOfFile(pHeader);
401                 }
402                 else
403                 {
404                     dwError = GetLastError();
405                 }
406                 CloseHandle(hMapping);
407             }
408             else
409             {
410                 dwError = GetLastError();
411             }
412         }
413
414         if (dwError)
415         {
416             CloseHandle(hFile);
417             DeleteFileW(wszFilePath);
418             ReleaseMutex(pContainer->hMutex);
419             SetLastError(dwError);
420             return FALSE;
421         }
422
423     }
424
425     ReleaseMutex(pContainer->hMutex);
426
427     wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
428     URLCache_PathToObjectName(wszFilePath, '_');
429     pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
430     if (!pContainer->hMapping)
431         pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
432     CloseHandle(hFile);
433     if (!pContainer->hMapping)
434     {
435         ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
436         return FALSE;
437     }
438
439     return TRUE;
440 }
441
442 /***********************************************************************
443  *           URLCacheContainer_CloseIndex (Internal)
444  *
445  *  Closes the index
446  *
447  * RETURNS
448  *    nothing
449  *
450  */
451 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
452 {
453     CloseHandle(pContainer->hMapping);
454     pContainer->hMapping = NULL;
455 }
456
457 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
458 {
459     URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
460     int path_len = strlenW(path);
461     int cache_prefix_len = strlenW(cache_prefix);
462
463     if (!pContainer)
464     {
465         return FALSE;
466     }
467
468     pContainer->hMapping = NULL;
469     pContainer->file_size = 0;
470
471     pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
472     if (!pContainer->path)
473     {
474         HeapFree(GetProcessHeap(), 0, pContainer);
475         return FALSE;
476     }
477
478     memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
479
480     pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
481     if (!pContainer->cache_prefix)
482     {
483         HeapFree(GetProcessHeap(), 0, pContainer->path);
484         HeapFree(GetProcessHeap(), 0, pContainer);
485         return FALSE;
486     }
487
488     memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
489
490     CharLowerW(mutex_name);
491     URLCache_PathToObjectName(mutex_name, '!');
492
493     if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
494     {
495         ERR("couldn't create mutex (error is %d)\n", GetLastError());
496         HeapFree(GetProcessHeap(), 0, pContainer->path);
497         HeapFree(GetProcessHeap(), 0, pContainer);
498         return FALSE;
499     }
500
501     list_add_head(&UrlContainers, &pContainer->entry);
502
503     return TRUE;
504 }
505
506 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
507 {
508     list_remove(&pContainer->entry);
509
510     URLCacheContainer_CloseIndex(pContainer);
511     CloseHandle(pContainer->hMutex);
512     HeapFree(GetProcessHeap(), 0, pContainer->path);
513     HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
514     HeapFree(GetProcessHeap(), 0, pContainer);
515 }
516
517 void URLCacheContainers_CreateDefaults(void)
518 {
519     static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
520     static const WCHAR UrlPrefix[] = {0};
521     static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
522     static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
523     static const WCHAR CookieSuffix[] = {0};
524     static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
525     static const struct
526     {
527         int nFolder; /* CSIDL_* constant */
528         const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
529         const WCHAR * cache_prefix; /* prefix used to reference the container */
530     } DefaultContainerData[] = 
531     {
532         { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
533         { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
534         { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
535     };
536     DWORD i;
537
538     for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
539     {
540         WCHAR wszCachePath[MAX_PATH];
541         WCHAR wszMutexName[MAX_PATH];
542         int path_len, suffix_len;
543
544         if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
545         {
546             ERR("Couldn't get path for default container %u\n", i);
547             continue;
548         }
549         path_len = strlenW(wszCachePath);
550         suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
551
552         if (path_len + suffix_len + 2 > MAX_PATH)
553         {
554             ERR("Path too long\n");
555             continue;
556         }
557
558         wszCachePath[path_len] = '\\';
559         wszCachePath[path_len+1] = 0;
560
561         strcpyW(wszMutexName, wszCachePath);
562         
563         if (suffix_len)
564         {
565             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
566             wszCachePath[path_len + suffix_len + 1] = '\\';
567             wszCachePath[path_len + suffix_len + 2] = '\0';
568         }
569
570         URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
571     }
572 }
573
574 void URLCacheContainers_DeleteAll(void)
575 {
576     while(!list_empty(&UrlContainers))
577         URLCacheContainer_DeleteContainer(
578             LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
579         );
580 }
581
582 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
583 {
584     URLCACHECONTAINER * pContainer;
585
586     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
587
588     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
589     {
590         int prefix_len = strlenW(pContainer->cache_prefix);
591         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
592         {
593             TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
594             *ppContainer = pContainer;
595             return TRUE;
596         }
597     }
598     ERR("no container found\n");
599     SetLastError(ERROR_FILE_NOT_FOUND);
600     return FALSE;
601 }
602
603 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
604 {
605     BOOL ret;
606     LPWSTR lpwszUrl;
607     int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
608     if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
609     {
610         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
611         ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
612         HeapFree(GetProcessHeap(), 0, lpwszUrl);
613         return ret;
614     }
615     return FALSE;
616 }
617
618 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
619 {
620     DWORD i = 0;
621     URLCACHECONTAINER * pContainer;
622
623     TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
624
625     /* non-NULL search pattern only returns one container ever */
626     if (lpwszSearchPattern && dwIndex > 0)
627         return FALSE;
628
629     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
630     {
631         if (lpwszSearchPattern)
632         {
633             if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
634             {
635                 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
636                 *ppContainer = pContainer;
637                 return TRUE;
638             }
639         }
640         else
641         {
642             if (i == dwIndex)
643             {
644                 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
645                 *ppContainer = pContainer;
646                 return TRUE;
647             }
648         }
649         i++;
650     }
651     return FALSE;
652 }
653
654 /***********************************************************************
655  *           URLCacheContainer_LockIndex (Internal)
656  *
657  */
658 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
659 {
660     BYTE index;
661     LPVOID pIndexData;
662     URLCACHE_HEADER * pHeader;
663
664     /* acquire mutex */
665     WaitForSingleObject(pContainer->hMutex, INFINITE);
666
667     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
668
669     if (!pIndexData)
670     {
671         ReleaseMutex(pContainer->hMutex);
672         ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
673         return FALSE;
674     }
675     pHeader = (URLCACHE_HEADER *)pIndexData;
676
677     /* file has grown - we need to remap to prevent us getting
678      * access violations when we try and access beyond the end
679      * of the memory mapped file */
680     if (pHeader->dwFileSize != pContainer->file_size)
681     {
682         URLCacheContainer_CloseIndex(pContainer);
683         if (!URLCacheContainer_OpenIndex(pContainer))
684         {
685             ReleaseMutex(pContainer->hMutex);
686             return FALSE;
687         }
688         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
689
690         if (!pIndexData)
691         {
692             ReleaseMutex(pContainer->hMutex);
693             ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
694             return FALSE;
695         }
696         pHeader = (URLCACHE_HEADER *)pIndexData;
697     }
698
699     TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
700
701     for (index = 0; index < pHeader->DirectoryCount; index++)
702     {
703         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
704     }
705     
706     return pHeader;
707 }
708
709 /***********************************************************************
710  *           URLCacheContainer_UnlockIndex (Internal)
711  *
712  */
713 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
714 {
715     /* release mutex */
716     ReleaseMutex(pContainer->hMutex);
717     return UnmapViewOfFile(pHeader);
718 }
719
720
721 #ifndef CHAR_BIT
722 #define CHAR_BIT    (8 * sizeof(CHAR))
723 #endif
724
725 /***********************************************************************
726  *           URLCache_Allocation_BlockIsFree (Internal)
727  *
728  *  Is the specified block number free?
729  *
730  * RETURNS
731  *    zero if free
732  *    non-zero otherwise
733  *
734  */
735 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
736 {
737     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
738     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
739 }
740
741 /***********************************************************************
742  *           URLCache_Allocation_BlockFree (Internal)
743  *
744  *  Marks the specified block as free
745  *
746  * RETURNS
747  *    nothing
748  *
749  */
750 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
751 {
752     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
753     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
754 }
755
756 /***********************************************************************
757  *           URLCache_Allocation_BlockAllocate (Internal)
758  *
759  *  Marks the specified block as allocated
760  *
761  * RETURNS
762  *    nothing
763  *
764  */
765 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
766 {
767     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
768     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
769 }
770
771 /***********************************************************************
772  *           URLCache_FindFirstFreeEntry (Internal)
773  *
774  *  Finds and allocates the first block of free space big enough and
775  * sets ppEntry to point to it.
776  *
777  * RETURNS
778  *    TRUE if it had enough space
779  *    FALSE if it couldn't find enough space
780  *
781  */
782 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
783 {
784     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
785     DWORD dwBlockNumber;
786     DWORD dwFreeCounter;
787     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
788     {
789         for (dwFreeCounter = 0; 
790             dwFreeCounter < dwBlocksNeeded &&
791               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
792               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
793             dwFreeCounter++)
794                 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
795
796         if (dwFreeCounter == dwBlocksNeeded)
797         {
798             DWORD index;
799             TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
800             for (index = 0; index < dwBlocksNeeded; index++)
801                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
802             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
803             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
804             return TRUE;
805         }
806     }
807     FIXME("Grow file\n");
808     return FALSE;
809 }
810
811 /***********************************************************************
812  *           URLCache_DeleteEntry (Internal)
813  *
814  *  Deletes the specified entry and frees the space allocated to it
815  *
816  * RETURNS
817  *    TRUE if it succeeded
818  *    FALSE if it failed
819  *
820  */
821 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
822 {
823     DWORD dwStartBlock;
824     DWORD dwBlock;
825     BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
826
827     /* update allocation table */
828     dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
829     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
830         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
831
832     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
833     return TRUE;
834 }
835
836 /***********************************************************************
837  *           URLCache_LocalFileNameToPathW (Internal)
838  *
839  *  Copies the full path to the specified buffer given the local file
840  * name and the index of the directory it is in. Always sets value in
841  * lpBufferSize to the required buffer size (in bytes).
842  *
843  * RETURNS
844  *    TRUE if the buffer was big enough
845  *    FALSE if the buffer was too small
846  *
847  */
848 static BOOL URLCache_LocalFileNameToPathW(
849     const URLCACHECONTAINER * pContainer,
850     LPCURLCACHE_HEADER pHeader,
851     LPCSTR szLocalFileName,
852     BYTE Directory,
853     LPWSTR wszPath,
854     LPLONG lpBufferSize)
855 {
856     LONG nRequired;
857     int path_len = strlenW(pContainer->path);
858     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
859     if (Directory >= pHeader->DirectoryCount)
860     {
861         *lpBufferSize = 0;
862         return FALSE;
863     }
864
865     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
866     if (nRequired < *lpBufferSize)
867     {
868         int dir_len;
869
870         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
871         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
872         wszPath[dir_len + path_len] = '\\';
873         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
874         *lpBufferSize = nRequired;
875         return TRUE;
876     }
877     *lpBufferSize = nRequired;
878     return FALSE;
879 }
880
881 /***********************************************************************
882  *           URLCache_LocalFileNameToPathA (Internal)
883  *
884  *  Copies the full path to the specified buffer given the local file
885  * name and the index of the directory it is in. Always sets value in
886  * lpBufferSize to the required buffer size.
887  *
888  * RETURNS
889  *    TRUE if the buffer was big enough
890  *    FALSE if the buffer was too small
891  *
892  */
893 static BOOL URLCache_LocalFileNameToPathA(
894     const URLCACHECONTAINER * pContainer,
895     LPCURLCACHE_HEADER pHeader,
896     LPCSTR szLocalFileName,
897     BYTE Directory,
898     LPSTR szPath,
899     LPLONG lpBufferSize)
900 {
901     LONG nRequired;
902     int path_len, file_name_len, dir_len;
903
904     if (Directory >= pHeader->DirectoryCount)
905     {
906         *lpBufferSize = 0;
907         return FALSE;
908     }
909
910     path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
911     file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
912     dir_len = DIR_LENGTH;
913
914     nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
915     if (nRequired < *lpBufferSize)
916     {
917         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
918         memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
919         szPath[path_len + dir_len] = '\\';
920         memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
921         *lpBufferSize = nRequired;
922         return TRUE;
923     }
924     *lpBufferSize = nRequired;
925     return FALSE;
926 }
927
928 /***********************************************************************
929  *           URLCache_CopyEntry (Internal)
930  *
931  *  Copies an entry from the cache index file to the Win32 structure
932  *
933  * RETURNS
934  *    TRUE if the buffer was big enough
935  *    FALSE if the buffer was too small
936  *
937  */
938 static BOOL URLCache_CopyEntry(
939     URLCACHECONTAINER * pContainer,
940     LPCURLCACHE_HEADER pHeader, 
941     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
942     LPDWORD lpdwBufferSize, 
943     const URL_CACHEFILE_ENTRY * pUrlEntry,
944     BOOL bUnicode)
945 {
946     int lenUrl;
947     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
948
949     if (*lpdwBufferSize >= dwRequiredSize)
950     {
951         lpCacheEntryInfo->lpHeaderInfo = NULL;
952         lpCacheEntryInfo->lpszFileExtension = NULL;
953         lpCacheEntryInfo->lpszLocalFileName = NULL;
954         lpCacheEntryInfo->lpszSourceUrlName = NULL;
955         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
956         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
957         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
958         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
959         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
960         lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
961         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
962         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
963         DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
964         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
965         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
966         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
967         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
968         DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
969     }
970
971     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
972         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
973     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
974     if (bUnicode)
975         lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
976     else
977         lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
978     dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
979
980     /* FIXME: is source url optional? */
981     if (*lpdwBufferSize >= dwRequiredSize)
982     {
983         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
984         if (bUnicode)
985             MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
986         else
987             memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
988     }
989
990     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
991         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
992     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
993
994     if (pUrlEntry->dwOffsetLocalName)
995     {
996         LONG nLocalFilePathSize;
997         LPSTR lpszLocalFileName;
998         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
999         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1000         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1001             URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
1002         {
1003             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1004         }
1005         dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1006
1007         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1008             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1009         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1010     }
1011     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1012
1013     if (*lpdwBufferSize >= dwRequiredSize)
1014     {
1015         lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1016         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1017         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1018     }
1019     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1020         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1021     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1022
1023     if (pUrlEntry->dwOffsetFileExtension)
1024     {
1025         int lenExtension;
1026
1027         if (bUnicode)
1028             lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1029         else
1030             lenExtension = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1031         dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1032
1033         if (*lpdwBufferSize >= dwRequiredSize)
1034         {
1035             lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1036             if (bUnicode)
1037                 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1038             else
1039                 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1040         }
1041
1042         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1043             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1044         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1045     }
1046
1047     if (dwRequiredSize > *lpdwBufferSize)
1048     {
1049         *lpdwBufferSize = dwRequiredSize;
1050         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1051         return FALSE;
1052     }
1053     *lpdwBufferSize = dwRequiredSize;
1054     return TRUE;
1055 }
1056
1057
1058 /***********************************************************************
1059  *           URLCache_SetEntryInfo (Internal)
1060  *
1061  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1062  * according to the flags set by dwFieldControl.
1063  *
1064  * RETURNS
1065  *    TRUE if the buffer was big enough
1066  *    FALSE if the buffer was too small
1067  *
1068  */
1069 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1070 {
1071     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1072         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1073     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1074         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1075     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1076         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1077     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1078         FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1079     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1080         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1081     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1082         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1083     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1084         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1085     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1086         FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1087
1088     return TRUE;
1089 }
1090
1091 /***********************************************************************
1092  *           URLCache_HashKey (Internal)
1093  *
1094  *  Returns the hash key for a given string
1095  *
1096  * RETURNS
1097  *    hash key for the string
1098  *
1099  */
1100 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1101 {
1102     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1103      * but the algorithm and result are not the same!
1104      */
1105     static const unsigned char lookupTable[256] = 
1106     {
1107         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1108         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1109         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1110         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1111         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1112         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1113         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1114         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1115         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1116         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1117         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1118         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1119         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1120         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1121         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1122         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1123         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1124         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1125         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1126         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1127         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1128         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1129         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1130         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1131         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1132         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1133         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1134         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1135         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1136         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1137         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1138         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1139     };
1140     BYTE key[4];
1141     DWORD i;
1142     int subscript[sizeof(key) / sizeof(key[0])];
1143
1144     subscript[0] = *lpszKey;
1145     subscript[1] = (char)(*lpszKey + 1);
1146     subscript[2] = (char)(*lpszKey + 2);
1147     subscript[3] = (char)(*lpszKey + 3);
1148
1149     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1150         key[i] = lookupTable[i];
1151
1152     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1153     {
1154         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1155             key[i] = lookupTable[*lpszKey ^ key[i]];
1156     }
1157
1158     return *(DWORD *)key;
1159 }
1160
1161 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1162 {
1163     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1164 }
1165
1166 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1167 {
1168     /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1169     return ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) &&
1170            ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1171 }
1172
1173 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1174 {
1175     /* structure of hash table:
1176      *  448 entries divided into 64 blocks
1177      *  each block therefore contains a chain of 7 key/offset pairs
1178      * how position in table is calculated:
1179      *  1. the url is hashed in helper function
1180      *  2. the key % 64 * 8 is the offset
1181      *  3. the key in the hash table is the hash key aligned to 64
1182      *
1183      * note:
1184      *  there can be multiple hash tables in the file and the offset to
1185      *  the next one is stored in the header of the hash table
1186      */
1187     DWORD key = URLCache_HashKey(lpszUrl);
1188     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1189     HASH_CACHEFILE_ENTRY * pHashEntry;
1190     DWORD dwHashTableNumber = 0;
1191
1192     key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1193
1194     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1195          URLCache_IsHashEntryValid(pHeader, pHashEntry);
1196          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1197     {
1198         int i;
1199         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1200         {
1201             ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1202             continue;
1203         }
1204         /* make sure that it is in fact a hash entry */
1205         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1206         {
1207             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1208             continue;
1209         }
1210
1211         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1212         {
1213             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1214             if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1215             {
1216                 /* FIXME: we should make sure that this is the right element
1217                  * before returning and claiming that it is. We can do this
1218                  * by doing a simple compare between the URL we were given
1219                  * and the URL stored in the entry. However, this assumes
1220                  * we know the format of all the entries stored in the
1221                  * hash table */
1222                 *ppHashEntry = pHashElement;
1223                 return TRUE;
1224             }
1225         }
1226     }
1227     return FALSE;
1228 }
1229
1230 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1231 {
1232     LPSTR urlA;
1233     int url_len;
1234     BOOL ret;
1235
1236     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1237     urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1238     if (!urlA)
1239     {
1240         SetLastError(ERROR_OUTOFMEMORY);
1241         return FALSE;
1242     }
1243     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, urlA, url_len, NULL, NULL);
1244     ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1245     HeapFree(GetProcessHeap(), 0, urlA);
1246     return ret;
1247 }
1248
1249 /***********************************************************************
1250  *           URLCache_HashEntrySetUse (Internal)
1251  *
1252  *  Searches all the hash tables in the index for the given URL and
1253  * sets the use count (stored or'ed with key)
1254  *
1255  * RETURNS
1256  *    TRUE if the entry was found
1257  *    FALSE if the entry could not be found
1258  *
1259  */
1260 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1261 {
1262     pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1263     return TRUE;
1264 }
1265
1266 /***********************************************************************
1267  *           URLCache_DeleteEntryFromHash (Internal)
1268  *
1269  *  Searches all the hash tables in the index for the given URL and
1270  * then if found deletes the entry.
1271  *
1272  * RETURNS
1273  *    TRUE if the entry was found
1274  *    FALSE if the entry could not be found
1275  *
1276  */
1277 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1278 {
1279     pHashEntry->dwHashKey = HASHTABLE_FREE;
1280     pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1281     return TRUE;
1282 }
1283
1284 /***********************************************************************
1285  *           URLCache_AddEntryToHash (Internal)
1286  *
1287  *  Searches all the hash tables for a free slot based on the offset
1288  * generated from the hash key. If a free slot is found, the offset and
1289  * key are entered into the hash table.
1290  *
1291  * RETURNS
1292  *    TRUE if the entry was added
1293  *    FALSE if the entry could not be added
1294  *
1295  */
1296 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1297 {
1298     /* see URLCache_FindEntryInHash for structure of hash tables */
1299
1300     DWORD key = URLCache_HashKey(lpszUrl);
1301     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1302     HASH_CACHEFILE_ENTRY * pHashEntry;
1303     DWORD dwHashTableNumber = 0;
1304
1305     key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1306
1307     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1308          URLCache_IsHashEntryValid(pHeader, pHashEntry);
1309          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1310     {
1311         int i;
1312         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1313         {
1314             ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1315             break;
1316         }
1317         /* make sure that it is in fact a hash entry */
1318         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1319         {
1320             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1321             break;
1322         }
1323
1324         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1325         {
1326             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1327             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1328             {
1329                 pHashElement->dwHashKey = key;
1330                 pHashElement->dwOffsetEntry = dwOffsetEntry;
1331                 return TRUE;
1332             }
1333         }
1334     }
1335     pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1336     if (!pHashEntry)
1337         return FALSE;
1338
1339     pHashEntry->HashTable[offset].dwHashKey = key;
1340     pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1341     return TRUE;
1342 }
1343
1344 /***********************************************************************
1345  *           URLCache_CreateHashTable (Internal)
1346  *
1347  *  Creates a new hash table in free space and adds it to the chain of existing
1348  * hash tables.
1349  *
1350  * RETURNS
1351  *    TRUE if the hash table was created
1352  *    FALSE if the hash table could not be created
1353  *
1354  */
1355 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1356 {
1357     HASH_CACHEFILE_ENTRY *pHash;
1358     DWORD dwOffset;
1359     int i;
1360
1361     if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1362     {
1363         FIXME("no free space for hash table\n");
1364         SetLastError(ERROR_DISK_FULL);
1365         return NULL;
1366     }
1367
1368     dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1369
1370     if (pPrevHash)
1371         pPrevHash->dwAddressNext = dwOffset;
1372     else
1373         pHeader->dwOffsetFirstHashTable = dwOffset;
1374     pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1375     pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1376     pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1377     for (i = 0; i < HASHTABLE_SIZE; i++)
1378     {
1379         pHash->HashTable[i].dwOffsetEntry = 0;
1380         pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1381     }
1382     return pHash;
1383 }
1384
1385 /***********************************************************************
1386  *           URLCache_EnumHashTables (Internal)
1387  *
1388  *  Enumerates the hash tables in a container.
1389  *
1390  * RETURNS
1391  *    TRUE if an entry was found
1392  *    FALSE if there are no more tables to enumerate.
1393  *
1394  */
1395 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1396 {
1397     for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1398          URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1399          *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1400     {
1401         TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1402         if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1403             continue;
1404         /* make sure that it is in fact a hash entry */
1405         if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1406         {
1407             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1408             (*pdwHashTableNumber)++;
1409             continue;
1410         }
1411
1412         TRACE("hash table number %d found\n", *pdwHashTableNumber);
1413         return TRUE;
1414     }
1415     return FALSE;
1416 }
1417
1418 /***********************************************************************
1419  *           URLCache_EnumHashTableEntries (Internal)
1420  *
1421  *  Enumerates entries in a hash table and returns the next non-free entry.
1422  *
1423  * RETURNS
1424  *    TRUE if an entry was found
1425  *    FALSE if the hash table is empty or there are no more entries to
1426  *     enumerate.
1427  *
1428  */
1429 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1430                                           DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1431 {
1432     for (; *index < HASHTABLE_SIZE ; (*index)++)
1433     {
1434         if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1435             continue;
1436
1437         *ppHashEntry = &pHashEntry->HashTable[*index];
1438         TRACE("entry found %d\n", *index);
1439         return TRUE;
1440     }
1441     TRACE("no more entries (%d)\n", *index);
1442     return FALSE;
1443 }
1444
1445 /***********************************************************************
1446  *           GetUrlCacheEntryInfoExA (WININET.@)
1447  *
1448  */
1449 BOOL WINAPI GetUrlCacheEntryInfoExA(
1450     LPCSTR lpszUrl,
1451     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1452     LPDWORD lpdwCacheEntryInfoBufSize,
1453     LPSTR lpszReserved,
1454     LPDWORD lpdwReserved,
1455     LPVOID lpReserved,
1456     DWORD dwFlags)
1457 {
1458     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1459         debugstr_a(lpszUrl), 
1460         lpCacheEntryInfo,
1461         lpdwCacheEntryInfoBufSize,
1462         lpszReserved,
1463         lpdwReserved,
1464         lpReserved,
1465         dwFlags);
1466
1467     if ((lpszReserved != NULL) ||
1468         (lpdwReserved != NULL) ||
1469         (lpReserved != NULL))
1470     {
1471         ERR("Reserved value was not 0\n");
1472         SetLastError(ERROR_INVALID_PARAMETER);
1473         return FALSE;
1474     }
1475     if (dwFlags != 0)
1476         FIXME("Undocumented flag(s): %x\n", dwFlags);
1477     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1478 }
1479
1480 /***********************************************************************
1481  *           GetUrlCacheEntryInfoA (WININET.@)
1482  *
1483  */
1484 BOOL WINAPI GetUrlCacheEntryInfoA(
1485     IN LPCSTR lpszUrlName,
1486     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1487     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1488 )
1489 {
1490     LPURLCACHE_HEADER pHeader;
1491     struct _HASH_ENTRY * pHashEntry;
1492     const CACHEFILE_ENTRY * pEntry;
1493     const URL_CACHEFILE_ENTRY * pUrlEntry;
1494     URLCACHECONTAINER * pContainer;
1495
1496     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1497
1498     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1499         return FALSE;
1500
1501     if (!URLCacheContainer_OpenIndex(pContainer))
1502         return FALSE;
1503
1504     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1505         return FALSE;
1506
1507     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1508     {
1509         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1510         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1511         SetLastError(ERROR_FILE_NOT_FOUND);
1512         return FALSE;
1513     }
1514
1515     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1516     if (pEntry->dwSignature != URL_SIGNATURE)
1517     {
1518         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1519         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1520         SetLastError(ERROR_FILE_NOT_FOUND);
1521         return FALSE;
1522     }
1523
1524     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1525     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1526     if (pUrlEntry->dwOffsetHeaderInfo)
1527         TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1528
1529     if (lpdwCacheEntryInfoBufferSize)
1530     {
1531         if (!URLCache_CopyEntry(
1532             pContainer,
1533             pHeader,
1534             lpCacheEntryInfo,
1535             lpdwCacheEntryInfoBufferSize,
1536             pUrlEntry,
1537             FALSE /* ANSI */))
1538         {
1539             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1540             return FALSE;
1541         }
1542         TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1543     }
1544
1545     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1546
1547     return TRUE;
1548 }
1549
1550 /***********************************************************************
1551  *           GetUrlCacheEntryInfoW (WININET.@)
1552  *
1553  */
1554 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1555   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1556   LPDWORD lpdwCacheEntryInfoBufferSize)
1557 {
1558     LPURLCACHE_HEADER pHeader;
1559     struct _HASH_ENTRY * pHashEntry;
1560     const CACHEFILE_ENTRY * pEntry;
1561     const URL_CACHEFILE_ENTRY * pUrlEntry;
1562     URLCACHECONTAINER * pContainer;
1563
1564     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1565
1566     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1567         return FALSE;
1568
1569     if (!URLCacheContainer_OpenIndex(pContainer))
1570         return FALSE;
1571
1572     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1573         return FALSE;
1574
1575     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1576     {
1577         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1578         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1579         SetLastError(ERROR_FILE_NOT_FOUND);
1580         return FALSE;
1581     }
1582
1583     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1584     if (pEntry->dwSignature != URL_SIGNATURE)
1585     {
1586         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1587         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1588         SetLastError(ERROR_FILE_NOT_FOUND);
1589         return FALSE;
1590     }
1591
1592     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1593     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1594     TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1595
1596     if (lpdwCacheEntryInfoBufferSize)
1597     {
1598         if (!URLCache_CopyEntry(
1599             pContainer,
1600             pHeader,
1601             (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1602             lpdwCacheEntryInfoBufferSize,
1603             pUrlEntry,
1604             TRUE /* UNICODE */))
1605         {
1606             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1607             return FALSE;
1608         }
1609         TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1610     }
1611
1612     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1613
1614     return TRUE;
1615 }
1616
1617 /***********************************************************************
1618  *           GetUrlCacheEntryInfoExW (WININET.@)
1619  *
1620  */
1621 BOOL WINAPI GetUrlCacheEntryInfoExW(
1622     LPCWSTR lpszUrl,
1623     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1624     LPDWORD lpdwCacheEntryInfoBufSize,
1625     LPWSTR lpszReserved,
1626     LPDWORD lpdwReserved,
1627     LPVOID lpReserved,
1628     DWORD dwFlags)
1629 {
1630     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1631         debugstr_w(lpszUrl), 
1632         lpCacheEntryInfo,
1633         lpdwCacheEntryInfoBufSize,
1634         lpszReserved,
1635         lpdwReserved,
1636         lpReserved,
1637         dwFlags);
1638
1639     if ((lpszReserved != NULL) ||
1640         (lpdwReserved != NULL) ||
1641         (lpReserved != NULL))
1642     {
1643         ERR("Reserved value was not 0\n");
1644         SetLastError(ERROR_INVALID_PARAMETER);
1645         return FALSE;
1646     }
1647     if (dwFlags != 0)
1648         FIXME("Undocumented flag(s): %x\n", dwFlags);
1649     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1650 }
1651
1652 /***********************************************************************
1653  *           SetUrlCacheEntryInfoA (WININET.@)
1654  */
1655 BOOL WINAPI SetUrlCacheEntryInfoA(
1656     LPCSTR lpszUrlName,
1657     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1658     DWORD dwFieldControl)
1659 {
1660     LPURLCACHE_HEADER pHeader;
1661     struct _HASH_ENTRY * pHashEntry;
1662     CACHEFILE_ENTRY * pEntry;
1663     URLCACHECONTAINER * pContainer;
1664
1665     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1666
1667     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1668         return FALSE;
1669
1670     if (!URLCacheContainer_OpenIndex(pContainer))
1671         return FALSE;
1672
1673     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1674         return FALSE;
1675
1676     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1677     {
1678         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1679         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1680         SetLastError(ERROR_FILE_NOT_FOUND);
1681         return FALSE;
1682     }
1683
1684     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1685     if (pEntry->dwSignature != URL_SIGNATURE)
1686     {
1687         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1688         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1689         SetLastError(ERROR_FILE_NOT_FOUND);
1690         return FALSE;
1691     }
1692
1693     URLCache_SetEntryInfo(
1694         (URL_CACHEFILE_ENTRY *)pEntry,
1695         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1696         dwFieldControl);
1697
1698     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1699
1700     return TRUE;
1701 }
1702
1703 /***********************************************************************
1704  *           SetUrlCacheEntryInfoW (WININET.@)
1705  */
1706 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1707 {
1708     LPURLCACHE_HEADER pHeader;
1709     struct _HASH_ENTRY * pHashEntry;
1710     CACHEFILE_ENTRY * pEntry;
1711     URLCACHECONTAINER * pContainer;
1712
1713     TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1714
1715     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1716         return FALSE;
1717
1718     if (!URLCacheContainer_OpenIndex(pContainer))
1719         return FALSE;
1720
1721     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1722         return FALSE;
1723
1724     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1725     {
1726         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1727         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1728         SetLastError(ERROR_FILE_NOT_FOUND);
1729         return FALSE;
1730     }
1731
1732     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1733     if (pEntry->dwSignature != URL_SIGNATURE)
1734     {
1735         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1736         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1737         SetLastError(ERROR_FILE_NOT_FOUND);
1738         return FALSE;
1739     }
1740
1741     URLCache_SetEntryInfo(
1742         (URL_CACHEFILE_ENTRY *)pEntry,
1743         lpCacheEntryInfo,
1744         dwFieldControl);
1745
1746     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1747
1748     return TRUE;
1749 }
1750
1751 /***********************************************************************
1752  *           RetrieveUrlCacheEntryFileA (WININET.@)
1753  *
1754  */
1755 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1756     IN LPCSTR lpszUrlName,
1757     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1758     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1759     IN DWORD dwReserved
1760     )
1761 {
1762     LPURLCACHE_HEADER pHeader;
1763     struct _HASH_ENTRY * pHashEntry;
1764     CACHEFILE_ENTRY * pEntry;
1765     URL_CACHEFILE_ENTRY * pUrlEntry;
1766     URLCACHECONTAINER * pContainer;
1767
1768     TRACE("(%s, %p, %p, 0x%08x)\n",
1769         debugstr_a(lpszUrlName),
1770         lpCacheEntryInfo,
1771         lpdwCacheEntryInfoBufferSize,
1772         dwReserved);
1773
1774     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1775         return FALSE;
1776
1777     if (!URLCacheContainer_OpenIndex(pContainer))
1778         return FALSE;
1779
1780     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1781         return FALSE;
1782
1783     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1784     {
1785         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1786         TRACE("entry %s not found!\n", lpszUrlName);
1787         SetLastError(ERROR_FILE_NOT_FOUND);
1788         return FALSE;
1789     }
1790
1791     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1792     if (pEntry->dwSignature != URL_SIGNATURE)
1793     {
1794         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1795         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1796         SetLastError(ERROR_FILE_NOT_FOUND);
1797         return FALSE;
1798     }
1799
1800     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1801     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1802     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1803
1804     pUrlEntry->dwHitRate++;
1805     pUrlEntry->dwUseCount++;
1806     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1807
1808     if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1809     {
1810         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1811         return FALSE;
1812     }
1813     TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1814
1815     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1816
1817     return TRUE;
1818 }
1819
1820 /***********************************************************************
1821  *           RetrieveUrlCacheEntryFileW (WININET.@)
1822  *
1823  */
1824 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1825     IN LPCWSTR lpszUrlName,
1826     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1827     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1828     IN DWORD dwReserved
1829     )
1830 {
1831     LPURLCACHE_HEADER pHeader;
1832     struct _HASH_ENTRY * pHashEntry;
1833     CACHEFILE_ENTRY * pEntry;
1834     URL_CACHEFILE_ENTRY * pUrlEntry;
1835     URLCACHECONTAINER * pContainer;
1836
1837     TRACE("(%s, %p, %p, 0x%08x)\n",
1838         debugstr_w(lpszUrlName),
1839         lpCacheEntryInfo,
1840         lpdwCacheEntryInfoBufferSize,
1841         dwReserved);
1842
1843     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1844         return FALSE;
1845
1846     if (!URLCacheContainer_OpenIndex(pContainer))
1847         return FALSE;
1848
1849     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1850         return FALSE;
1851
1852     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1853     {
1854         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1855         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1856         SetLastError(ERROR_FILE_NOT_FOUND);
1857         return FALSE;
1858     }
1859
1860     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1861     if (pEntry->dwSignature != URL_SIGNATURE)
1862     {
1863         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1864         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1865         SetLastError(ERROR_FILE_NOT_FOUND);
1866         return FALSE;
1867     }
1868
1869     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1870     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1871     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1872
1873     pUrlEntry->dwHitRate++;
1874     pUrlEntry->dwUseCount++;
1875     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1876
1877     if (!URLCache_CopyEntry(
1878         pContainer,
1879         pHeader,
1880         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1881         lpdwCacheEntryInfoBufferSize,
1882         pUrlEntry,
1883         TRUE /* UNICODE */))
1884     {
1885         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1886         return FALSE;
1887     }
1888     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1889
1890     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1891
1892     return TRUE;
1893 }
1894
1895 /***********************************************************************
1896  *           UnlockUrlCacheEntryFileA (WININET.@)
1897  *
1898  */
1899 BOOL WINAPI UnlockUrlCacheEntryFileA(
1900     IN LPCSTR lpszUrlName, 
1901     IN DWORD dwReserved
1902     )
1903 {
1904     LPURLCACHE_HEADER pHeader;
1905     struct _HASH_ENTRY * pHashEntry;
1906     CACHEFILE_ENTRY * pEntry;
1907     URL_CACHEFILE_ENTRY * pUrlEntry;
1908     URLCACHECONTAINER * pContainer;
1909
1910     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1911
1912     if (dwReserved)
1913     {
1914         ERR("dwReserved != 0\n");
1915         SetLastError(ERROR_INVALID_PARAMETER);
1916         return FALSE;
1917     }
1918
1919     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1920        return FALSE;
1921
1922     if (!URLCacheContainer_OpenIndex(pContainer))
1923         return FALSE;
1924
1925     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1926         return FALSE;
1927
1928     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1929     {
1930         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931         TRACE("entry %s not found!\n", lpszUrlName);
1932         SetLastError(ERROR_FILE_NOT_FOUND);
1933         return FALSE;
1934     }
1935
1936     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1937     if (pEntry->dwSignature != URL_SIGNATURE)
1938     {
1939         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1940         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1941         SetLastError(ERROR_FILE_NOT_FOUND);
1942         return FALSE;
1943     }
1944
1945     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1946
1947     if (pUrlEntry->dwUseCount == 0)
1948     {
1949         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1950         return FALSE;
1951     }
1952     pUrlEntry->dwUseCount--;
1953     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1954
1955     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1956
1957     return TRUE;
1958 }
1959
1960 /***********************************************************************
1961  *           UnlockUrlCacheEntryFileW (WININET.@)
1962  *
1963  */
1964 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1965 {
1966     LPURLCACHE_HEADER pHeader;
1967     struct _HASH_ENTRY * pHashEntry;
1968     CACHEFILE_ENTRY * pEntry;
1969     URL_CACHEFILE_ENTRY * pUrlEntry;
1970     URLCACHECONTAINER * pContainer;
1971
1972     TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1973
1974     if (dwReserved)
1975     {
1976         ERR("dwReserved != 0\n");
1977         SetLastError(ERROR_INVALID_PARAMETER);
1978         return FALSE;
1979     }
1980
1981     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1982        return FALSE;
1983
1984     if (!URLCacheContainer_OpenIndex(pContainer))
1985         return FALSE;
1986
1987     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1988         return FALSE;
1989
1990     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1991     {
1992         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1993         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1994         SetLastError(ERROR_FILE_NOT_FOUND);
1995         return FALSE;
1996     }
1997
1998     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1999     if (pEntry->dwSignature != URL_SIGNATURE)
2000     {
2001         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2002         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2003         SetLastError(ERROR_FILE_NOT_FOUND);
2004         return FALSE;
2005     }
2006
2007     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2008
2009     if (pUrlEntry->dwUseCount == 0)
2010     {
2011         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2012         return FALSE;
2013     }
2014     pUrlEntry->dwUseCount--;
2015     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2016
2017     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2018
2019     return TRUE;
2020 }
2021
2022 /***********************************************************************
2023  *           CreateUrlCacheEntryA (WININET.@)
2024  *
2025  */
2026 BOOL WINAPI CreateUrlCacheEntryA(
2027     IN LPCSTR lpszUrlName,
2028     IN DWORD dwExpectedFileSize,
2029     IN LPCSTR lpszFileExtension,
2030     OUT LPSTR lpszFileName,
2031     IN DWORD dwReserved
2032 )
2033 {
2034     DWORD len;
2035     WCHAR *url_name;
2036     WCHAR *file_extension;
2037     WCHAR file_name[MAX_PATH];
2038     BOOL bSuccess = FALSE;
2039     DWORD dwError = 0;
2040
2041     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2042         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2043     {
2044         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2045         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2046             (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2047         {
2048             MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2049             if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2050             {
2051                 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2052                 {
2053                     bSuccess = TRUE;
2054                 }
2055                 else
2056                 {
2057                     dwError = GetLastError();
2058                 }
2059             }
2060             else
2061             {
2062                 dwError = GetLastError();
2063             }
2064             HeapFree(GetProcessHeap(), 0, file_extension);
2065         }
2066         else
2067         {
2068             dwError = GetLastError();
2069         }
2070         HeapFree(GetProcessHeap(), 0, url_name);
2071         if (!bSuccess)
2072             SetLastError(dwError);
2073     }
2074     return bSuccess;
2075 }
2076 /***********************************************************************
2077  *           CreateUrlCacheEntryW (WININET.@)
2078  *
2079  */
2080 BOOL WINAPI CreateUrlCacheEntryW(
2081     IN LPCWSTR lpszUrlName,
2082     IN DWORD dwExpectedFileSize,
2083     IN LPCWSTR lpszFileExtension,
2084     OUT LPWSTR lpszFileName,
2085     IN DWORD dwReserved
2086 )
2087 {
2088     URLCACHECONTAINER * pContainer;
2089     LPURLCACHE_HEADER pHeader;
2090     CHAR szFile[MAX_PATH];
2091     WCHAR szExtension[MAX_PATH];
2092     LPCWSTR lpszUrlPart;
2093     LPCWSTR lpszUrlEnd;
2094     LPCWSTR lpszFileNameExtension;
2095     LPWSTR lpszFileNameNoPath;
2096     int i;
2097     int countnoextension;
2098     BYTE CacheDir;
2099     LONG lBufferSize;
2100     BOOL bFound = FALSE;
2101     int count;
2102     static const WCHAR szWWW[] = {'w','w','w',0};
2103
2104     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2105         debugstr_w(lpszUrlName),
2106         dwExpectedFileSize,
2107         debugstr_w(lpszFileExtension),
2108         lpszFileName,
2109         dwReserved);
2110
2111     if (dwReserved)
2112     {
2113         ERR("dwReserved != 0\n");
2114         SetLastError(ERROR_INVALID_PARAMETER);
2115         return FALSE;
2116     }
2117
2118    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2119     
2120     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2121         lpszUrlEnd--;
2122
2123     for (lpszUrlPart = lpszUrlEnd; 
2124         (lpszUrlPart >= lpszUrlName); 
2125         lpszUrlPart--)
2126     {
2127         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2128         {
2129             bFound = TRUE;
2130             lpszUrlPart++;
2131             break;
2132         }
2133         else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2134         {
2135             lpszUrlEnd = lpszUrlPart;
2136         }
2137     }
2138     if (!lstrcmpW(lpszUrlPart, szWWW))
2139     {
2140         lpszUrlPart += lstrlenW(szWWW);
2141     }
2142
2143     count = lpszUrlEnd - lpszUrlPart;
2144
2145     if (bFound && (count < MAX_PATH))
2146     {
2147         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2148         if (!len)
2149             return FALSE;
2150         szFile[len] = '\0';
2151         /* FIXME: get rid of illegal characters like \, / and : */
2152     }
2153     else
2154     {
2155         FIXME("need to generate a random filename\n");
2156     }
2157
2158     TRACE("File name: %s\n", debugstr_a(szFile));
2159
2160     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2161         return FALSE;
2162
2163     if (!URLCacheContainer_OpenIndex(pContainer))
2164         return FALSE;
2165
2166     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2167         return FALSE;
2168
2169     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2170
2171     lBufferSize = MAX_PATH * sizeof(WCHAR);
2172     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2173
2174     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2175
2176     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2177         lpszFileNameNoPath >= lpszFileName; 
2178         --lpszFileNameNoPath)
2179     {
2180         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2181             break;
2182     }
2183
2184     countnoextension = lstrlenW(lpszFileNameNoPath);
2185     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2186     if (lpszFileNameExtension)
2187         countnoextension -= lstrlenW(lpszFileNameExtension);
2188     *szExtension = '\0';
2189
2190     if (lpszFileExtension)
2191     {
2192         szExtension[0] = '.';
2193         lstrcpyW(szExtension+1, lpszFileExtension);
2194     }
2195
2196     for (i = 0; i < 255; i++)
2197     {
2198         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2199         HANDLE hFile;
2200         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2201         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2202         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2203         if (hFile != INVALID_HANDLE_VALUE)
2204         {
2205             CloseHandle(hFile);
2206             return TRUE;
2207         }
2208     }
2209
2210     return FALSE;
2211 }
2212
2213
2214 /***********************************************************************
2215  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2216  *
2217  *   The bug we are compensating for is that some drongo at Microsoft
2218  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2219  *   As a consequence, CommitUrlCacheEntryA has been effectively
2220  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2221  *   is still defined as LPCWSTR. The result (other than madness) is
2222  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2223  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2224  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2225  *   result will lose data for arbitrary binary data.
2226  *
2227  */
2228 static BOOL WINAPI CommitUrlCacheEntryInternal(
2229     IN LPCWSTR lpszUrlName,
2230     IN LPCWSTR lpszLocalFileName,
2231     IN FILETIME ExpireTime,
2232     IN FILETIME LastModifiedTime,
2233     IN DWORD CacheEntryType,
2234     IN LPBYTE lpHeaderInfo,
2235     IN DWORD dwHeaderSize,
2236     IN LPCWSTR lpszFileExtension,
2237     IN LPCWSTR lpszOriginalUrl
2238     )
2239 {
2240     URLCACHECONTAINER * pContainer;
2241     LPURLCACHE_HEADER pHeader;
2242     struct _HASH_ENTRY * pHashEntry;
2243     CACHEFILE_ENTRY * pEntry;
2244     URL_CACHEFILE_ENTRY * pUrlEntry;
2245     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2246     DWORD dwOffsetLocalFileName = 0;
2247     DWORD dwOffsetHeader = 0;
2248     DWORD dwOffsetFileExtension = 0;
2249     DWORD dwFileSizeLow = 0;
2250     DWORD dwFileSizeHigh = 0;
2251     BYTE cDirectory = 0;
2252     int len;
2253     char achFile[MAX_PATH];
2254     LPSTR lpszUrlNameA = NULL;
2255     LPSTR lpszFileExtensionA = NULL;
2256     char *pchLocalFileName = 0;
2257     DWORD error = ERROR_SUCCESS;
2258
2259     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2260         debugstr_w(lpszUrlName),
2261         debugstr_w(lpszLocalFileName),
2262         CacheEntryType,
2263         lpHeaderInfo,
2264         dwHeaderSize,
2265         debugstr_w(lpszFileExtension),
2266         debugstr_w(lpszOriginalUrl));
2267
2268     if (lpszOriginalUrl)
2269         WARN(": lpszOriginalUrl ignored\n");
2270  
2271     if (lpszLocalFileName)
2272     {
2273         HANDLE hFile;
2274
2275         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2276         if (hFile == INVALID_HANDLE_VALUE)
2277         {
2278             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2279             return FALSE;
2280         }
2281
2282         /* Get file size */
2283         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2284         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2285         {
2286             ERR("couldn't get file size (error is %d)\n", GetLastError());
2287             CloseHandle(hFile);
2288             return FALSE;
2289         }
2290
2291         CloseHandle(hFile);
2292     }
2293
2294     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2295         return FALSE;
2296
2297     if (!URLCacheContainer_OpenIndex(pContainer))
2298         return FALSE;
2299
2300     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2301         return FALSE;
2302
2303     len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2304     lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2305     if (!lpszUrlNameA)
2306     {
2307         error = GetLastError();
2308         goto cleanup;
2309     }
2310     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2311
2312     if (lpszFileExtension)
2313     {
2314         len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2315         lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2316         if (!lpszFileExtensionA)
2317         {
2318             error = GetLastError();
2319             goto cleanup;
2320         }
2321         WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2322     }
2323
2324     if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2325     {
2326         FIXME("entry already in cache - don't know what to do!\n");
2327 /*
2328  *        SetLastError(ERROR_FILE_NOT_FOUND);
2329  *        return FALSE;
2330  */
2331         goto cleanup;
2332     }
2333
2334     if (lpszLocalFileName)
2335     {
2336         BOOL bFound = FALSE;
2337
2338         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2339         {
2340             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2341             error = ERROR_INVALID_PARAMETER;
2342             goto cleanup;
2343         }
2344
2345         /* skip container path prefix */
2346         lpszLocalFileName += lstrlenW(pContainer->path);
2347
2348         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2349         pchLocalFileName = achFile;
2350
2351         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2352         {
2353             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2354             {
2355                 bFound = TRUE;
2356                 break;
2357             }
2358         }
2359
2360         if (!bFound)
2361         {
2362             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2363             error = ERROR_INVALID_PARAMETER;
2364             goto cleanup;
2365         }
2366
2367         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2368     }
2369
2370     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2371     if (lpszLocalFileName)
2372     {
2373         len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2374         dwOffsetLocalFileName = dwBytesNeeded;
2375         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2376     }
2377     if (lpHeaderInfo)
2378     {
2379         dwOffsetHeader = dwBytesNeeded;
2380         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2381     }
2382     if (lpszFileExtensionA)
2383     {
2384         dwOffsetFileExtension = dwBytesNeeded;
2385         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2386     }
2387
2388     /* round up to next block */
2389     if (dwBytesNeeded % BLOCKSIZE)
2390     {
2391         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2392         dwBytesNeeded += BLOCKSIZE;
2393     }
2394
2395     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2396     {
2397         ERR("no free entries\n");
2398         error = ERROR_DISK_FULL;
2399         goto cleanup;
2400     }
2401
2402     /* FindFirstFreeEntry fills in blocks used */
2403     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2404     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2405     pUrlEntry->CacheDir = cDirectory;
2406     pUrlEntry->CacheEntryType = CacheEntryType;
2407     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2408     pUrlEntry->dwExemptDelta = 0;
2409     pUrlEntry->dwHitRate = 0;
2410     pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2411     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2412     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2413     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2414     pUrlEntry->dwSizeHigh = 0;
2415     pUrlEntry->dwSizeLow = dwFileSizeLow;
2416     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2417     pUrlEntry->dwUseCount = 0;
2418     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2419     pUrlEntry->LastModifiedTime = LastModifiedTime;
2420     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2421     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2422     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2423     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2424
2425     /*** Unknowns ***/
2426     pUrlEntry->dwUnknown1 = 0;
2427     pUrlEntry->dwUnknown2 = 0;
2428     pUrlEntry->dwUnknown3 = 0x60;
2429     pUrlEntry->Unknown4 = 0;
2430     pUrlEntry->wUnknown5 = 0x1010;
2431     pUrlEntry->dwUnknown7 = 0;
2432     pUrlEntry->dwUnknown8 = 0;
2433
2434
2435     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2436     if (dwOffsetLocalFileName)
2437         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2438     if (dwOffsetHeader)
2439         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2440     if (dwOffsetFileExtension)
2441         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2442
2443     if (!URLCache_AddEntryToHash(pHeader, lpszUrlNameA, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2444     {
2445         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2446         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2447         HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2448         return FALSE;
2449     }
2450
2451 cleanup:
2452     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2453     HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2454     HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2455
2456     if (error == ERROR_SUCCESS)
2457         return TRUE;
2458     else
2459     {
2460         SetLastError(error);
2461         return FALSE;
2462     }
2463 }
2464
2465 /***********************************************************************
2466  *           CommitUrlCacheEntryA (WININET.@)
2467  *
2468  */
2469 BOOL WINAPI CommitUrlCacheEntryA(
2470     IN LPCSTR lpszUrlName,
2471     IN LPCSTR lpszLocalFileName,
2472     IN FILETIME ExpireTime,
2473     IN FILETIME LastModifiedTime,
2474     IN DWORD CacheEntryType,
2475     IN LPBYTE lpHeaderInfo,
2476     IN DWORD dwHeaderSize,
2477     IN LPCSTR lpszFileExtension,
2478     IN LPCSTR lpszOriginalUrl
2479     )
2480 {
2481     DWORD len;
2482     WCHAR *url_name = NULL;
2483     WCHAR *local_file_name = NULL;
2484     WCHAR *original_url = NULL;
2485     WCHAR *file_extension = NULL;
2486     BOOL bSuccess = FALSE;
2487
2488     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2489         debugstr_a(lpszUrlName),
2490         debugstr_a(lpszLocalFileName),
2491         CacheEntryType,
2492         lpHeaderInfo,
2493         dwHeaderSize,
2494         debugstr_a(lpszFileExtension),
2495         debugstr_a(lpszOriginalUrl));
2496
2497     len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2498     url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2499     if (!url_name)
2500         goto cleanup;
2501     MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2502
2503     if (lpszLocalFileName)
2504     {
2505         len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2506         local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2507         if (!local_file_name)
2508             goto cleanup;
2509         MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2510     }
2511     if (lpszFileExtension)
2512     {
2513         len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2514         file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2515         if (!file_extension)
2516             goto cleanup;
2517         MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2518     }
2519     if (lpszOriginalUrl)
2520     {
2521         len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2522         original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2523         if (!original_url)
2524             goto cleanup;
2525         MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2526     }
2527
2528     bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2529                                            CacheEntryType, lpHeaderInfo, dwHeaderSize,
2530                                            file_extension, original_url);
2531
2532 cleanup:
2533     HeapFree(GetProcessHeap(), 0, original_url);
2534     HeapFree(GetProcessHeap(), 0, file_extension);
2535     HeapFree(GetProcessHeap(), 0, local_file_name);
2536     HeapFree(GetProcessHeap(), 0, url_name);
2537
2538     return bSuccess;
2539 }
2540
2541 /***********************************************************************
2542  *           CommitUrlCacheEntryW (WININET.@)
2543  *
2544  */
2545 BOOL WINAPI CommitUrlCacheEntryW(
2546     IN LPCWSTR lpszUrlName,
2547     IN LPCWSTR lpszLocalFileName,
2548     IN FILETIME ExpireTime,
2549     IN FILETIME LastModifiedTime,
2550     IN DWORD CacheEntryType,
2551     IN LPWSTR lpHeaderInfo,
2552     IN DWORD dwHeaderSize,
2553     IN LPCWSTR lpszFileExtension,
2554     IN LPCWSTR lpszOriginalUrl
2555     )
2556 {
2557     DWORD dwError = 0;
2558     BOOL bSuccess = FALSE;
2559     DWORD len = 0;
2560     CHAR *header_info = NULL;
2561
2562     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2563         debugstr_w(lpszUrlName),
2564         debugstr_w(lpszLocalFileName),
2565         CacheEntryType,
2566         lpHeaderInfo,
2567         dwHeaderSize,
2568         debugstr_w(lpszFileExtension),
2569         debugstr_w(lpszOriginalUrl));
2570
2571     if (!lpHeaderInfo ||
2572         ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2573          (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2574     {
2575         if (header_info)
2576             WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2577         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2578                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2579         {
2580                 bSuccess = TRUE;
2581         }
2582         else
2583         {
2584                 dwError = GetLastError();
2585         }
2586         if (header_info)
2587         {
2588             HeapFree(GetProcessHeap(), 0, header_info);
2589             if (!bSuccess)
2590                 SetLastError(dwError);
2591         }
2592     }
2593     return bSuccess;
2594 }
2595
2596 /***********************************************************************
2597  *           ReadUrlCacheEntryStream (WININET.@)
2598  *
2599  */
2600 BOOL WINAPI ReadUrlCacheEntryStream(
2601     IN HANDLE hUrlCacheStream,
2602     IN  DWORD dwLocation,
2603     IN OUT LPVOID lpBuffer,
2604     IN OUT LPDWORD lpdwLen,
2605     IN DWORD dwReserved
2606     )
2607 {
2608     /* Get handle to file from 'stream' */
2609     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2610
2611     if (dwReserved != 0)
2612     {
2613         ERR("dwReserved != 0\n");
2614         SetLastError(ERROR_INVALID_PARAMETER);
2615         return FALSE;
2616     }
2617
2618     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2619     {
2620         SetLastError(ERROR_INVALID_HANDLE);
2621         return FALSE;
2622     }
2623
2624     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2625         return FALSE;
2626     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2627 }
2628
2629 /***********************************************************************
2630  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2631  *
2632  */
2633 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2634     IN LPCSTR lpszUrlName,
2635     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2636     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2637     IN BOOL fRandomRead,
2638     IN DWORD dwReserved
2639     )
2640 {
2641     /* NOTE: this is not the same as the way that the native
2642      * version allocates 'stream' handles. I did it this way
2643      * as it is much easier and no applications should depend
2644      * on this behaviour. (Native version appears to allocate
2645      * indices into a table)
2646      */
2647     STREAM_HANDLE * pStream;
2648     HANDLE hFile;
2649
2650     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2651            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2652
2653     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2654         lpCacheEntryInfo,
2655         lpdwCacheEntryInfoBufferSize,
2656         dwReserved))
2657     {
2658         return NULL;
2659     }
2660
2661     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2662         GENERIC_READ,
2663         FILE_SHARE_READ,
2664         NULL,
2665         OPEN_EXISTING,
2666         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2667         NULL);
2668     if (hFile == INVALID_HANDLE_VALUE)
2669         return FALSE;
2670     
2671     /* allocate handle storage space */
2672     pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2673     if (!pStream)
2674     {
2675         CloseHandle(hFile);
2676         SetLastError(ERROR_OUTOFMEMORY);
2677         return FALSE;
2678     }
2679
2680     pStream->hFile = hFile;
2681     strcpy(pStream->lpszUrl, lpszUrlName);
2682     return (HANDLE)pStream;
2683 }
2684
2685 /***********************************************************************
2686  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2687  *
2688  */
2689 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2690     IN LPCWSTR lpszUrlName,
2691     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2692     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2693     IN BOOL fRandomRead,
2694     IN DWORD dwReserved
2695     )
2696 {
2697     FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2698            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2699     return NULL;
2700 }
2701
2702 /***********************************************************************
2703  *           UnlockUrlCacheEntryStream (WININET.@)
2704  *
2705  */
2706 BOOL WINAPI UnlockUrlCacheEntryStream(
2707     IN HANDLE hUrlCacheStream,
2708     IN DWORD dwReserved
2709 )
2710 {
2711     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2712
2713     if (dwReserved != 0)
2714     {
2715         ERR("dwReserved != 0\n");
2716         SetLastError(ERROR_INVALID_PARAMETER);
2717         return FALSE;
2718     }
2719
2720     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2721     {
2722         SetLastError(ERROR_INVALID_HANDLE);
2723         return FALSE;
2724     }
2725
2726     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2727         return FALSE;
2728
2729     /* close file handle */
2730     CloseHandle(pStream->hFile);
2731
2732     /* free allocated space */
2733     HeapFree(GetProcessHeap(), 0, pStream);
2734
2735     return TRUE;
2736 }
2737
2738
2739 /***********************************************************************
2740  *           DeleteUrlCacheEntryA (WININET.@)
2741  *
2742  */
2743 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2744 {
2745     URLCACHECONTAINER * pContainer;
2746     LPURLCACHE_HEADER pHeader;
2747     struct _HASH_ENTRY * pHashEntry;
2748     CACHEFILE_ENTRY * pEntry;
2749
2750     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2751
2752     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2753         return FALSE;
2754
2755     if (!URLCacheContainer_OpenIndex(pContainer))
2756         return FALSE;
2757
2758     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2759         return FALSE;
2760
2761     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2762     {
2763         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2764         TRACE("entry %s not found!\n", lpszUrlName);
2765         SetLastError(ERROR_FILE_NOT_FOUND);
2766         return FALSE;
2767     }
2768
2769     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2770     URLCache_DeleteEntry(pHeader, pEntry);
2771
2772     URLCache_DeleteEntryFromHash(pHashEntry);
2773
2774     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2775
2776     return TRUE;
2777 }
2778
2779 /***********************************************************************
2780  *           DeleteUrlCacheEntryW (WININET.@)
2781  *
2782  */
2783 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2784 {
2785     URLCACHECONTAINER * pContainer;
2786     LPURLCACHE_HEADER pHeader;
2787     struct _HASH_ENTRY * pHashEntry;
2788     CACHEFILE_ENTRY * pEntry;
2789     LPSTR urlA;
2790     int url_len;
2791
2792     TRACE("(%s)\n", debugstr_w(lpszUrlName));
2793
2794     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2795     urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2796     if (!urlA)
2797     {
2798         SetLastError(ERROR_OUTOFMEMORY);
2799         return FALSE;
2800     }
2801     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2802
2803     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2804     {
2805         HeapFree(GetProcessHeap(), 0, urlA);
2806         return FALSE;
2807     }
2808     if (!URLCacheContainer_OpenIndex(pContainer))
2809     {
2810         HeapFree(GetProcessHeap(), 0, urlA);
2811         return FALSE;
2812     }
2813     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2814     {
2815         HeapFree(GetProcessHeap(), 0, urlA);
2816         return FALSE;
2817     }
2818
2819     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2820     {
2821         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2822         TRACE("entry %s not found!\n", debugstr_a(urlA));
2823         HeapFree(GetProcessHeap(), 0, urlA);
2824         SetLastError(ERROR_FILE_NOT_FOUND);
2825         return FALSE;
2826     }
2827
2828     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2829     URLCache_DeleteEntry(pHeader, pEntry);
2830
2831     URLCache_DeleteEntryFromHash(pHashEntry);
2832
2833     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2834
2835     HeapFree(GetProcessHeap(), 0, urlA);
2836     return TRUE;
2837 }
2838
2839 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2840 {
2841     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2842     return TRUE;
2843 }
2844
2845 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2846 {
2847     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2848     return TRUE;
2849 }
2850
2851 /***********************************************************************
2852  *           CreateCacheContainerA (WININET.@)
2853  */
2854 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2855                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2856 {
2857     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2858           d1, d2, d3, d4, d5, d6, d7, d8);
2859     return TRUE;
2860 }
2861
2862 /***********************************************************************
2863  *           CreateCacheContainerW (WININET.@)
2864  */
2865 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2866                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2867 {
2868     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2869           d1, d2, d3, d4, d5, d6, d7, d8);
2870     return TRUE;
2871 }
2872
2873 /***********************************************************************
2874  *           FindFirstUrlCacheContainerA (WININET.@)
2875  */
2876 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2877 {
2878     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2879     return NULL;
2880 }
2881
2882 /***********************************************************************
2883  *           FindFirstUrlCacheContainerW (WININET.@)
2884  */
2885 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2886 {
2887     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2888     return NULL;
2889 }
2890
2891 /***********************************************************************
2892  *           FindNextUrlCacheContainerA (WININET.@)
2893  */
2894 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2895 {
2896     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2897     return FALSE;
2898 }
2899
2900 /***********************************************************************
2901  *           FindNextUrlCacheContainerW (WININET.@)
2902  */
2903 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2904 {
2905     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2906     return FALSE;
2907 }
2908
2909 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2910   LPCSTR lpszUrlSearchPattern,
2911   DWORD dwFlags,
2912   DWORD dwFilter,
2913   GROUPID GroupId,
2914   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2915   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2916   LPVOID lpReserved,
2917   LPDWORD pcbReserved2,
2918   LPVOID lpReserved3
2919 )
2920 {
2921     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2922           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2923           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2924     SetLastError(ERROR_FILE_NOT_FOUND);
2925     return NULL;
2926 }
2927
2928 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2929   LPCWSTR lpszUrlSearchPattern,
2930   DWORD dwFlags,
2931   DWORD dwFilter,
2932   GROUPID GroupId,
2933   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2934   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2935   LPVOID lpReserved,
2936   LPDWORD pcbReserved2,
2937   LPVOID lpReserved3
2938 )
2939 {
2940     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2941           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2942           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2943     SetLastError(ERROR_FILE_NOT_FOUND);
2944     return NULL;
2945 }
2946
2947 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
2948
2949 typedef struct URLCacheFindEntryHandle
2950 {
2951     DWORD dwMagic;
2952     LPWSTR lpszUrlSearchPattern;
2953     DWORD dwContainerIndex;
2954     DWORD dwHashTableIndex;
2955     DWORD dwHashEntryIndex;
2956 } URLCacheFindEntryHandle;
2957
2958 /***********************************************************************
2959  *           FindFirstUrlCacheEntryA (WININET.@)
2960  *
2961  */
2962 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2963  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2964 {
2965     URLCacheFindEntryHandle *pEntryHandle;
2966
2967     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2968
2969     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
2970     if (!pEntryHandle)
2971         return NULL;
2972
2973     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
2974     if (lpszUrlSearchPattern)
2975     {
2976         int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
2977         pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2978         if (!pEntryHandle->lpszUrlSearchPattern)
2979         {
2980             HeapFree(GetProcessHeap(), 0, pEntryHandle);
2981             return NULL;
2982         }
2983         MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
2984     }
2985     else
2986         pEntryHandle->lpszUrlSearchPattern = NULL;
2987     pEntryHandle->dwContainerIndex = 0;
2988     pEntryHandle->dwHashTableIndex = 0;
2989     pEntryHandle->dwHashEntryIndex = 0;
2990
2991     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
2992     {
2993         HeapFree(GetProcessHeap(), 0, pEntryHandle);
2994         return NULL;
2995     }
2996     return pEntryHandle;
2997 }
2998
2999 /***********************************************************************
3000  *           FindFirstUrlCacheEntryW (WININET.@)
3001  *
3002  */
3003 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3004  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3005 {
3006     URLCacheFindEntryHandle *pEntryHandle;
3007
3008     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3009
3010     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3011     if (!pEntryHandle)
3012         return NULL;
3013
3014     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3015     if (lpszUrlSearchPattern)
3016     {
3017         int len = strlenW(lpszUrlSearchPattern);
3018         pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3019         if (!pEntryHandle->lpszUrlSearchPattern)
3020         {
3021             HeapFree(GetProcessHeap(), 0, pEntryHandle);
3022             return NULL;
3023         }
3024         memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3025     }
3026     else
3027         pEntryHandle->lpszUrlSearchPattern = NULL;
3028     pEntryHandle->dwContainerIndex = 0;
3029     pEntryHandle->dwHashTableIndex = 0;
3030     pEntryHandle->dwHashEntryIndex = 0;
3031
3032     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3033     {
3034         HeapFree(GetProcessHeap(), 0, pEntryHandle);
3035         return NULL;
3036     }
3037     return pEntryHandle;
3038 }
3039
3040 /***********************************************************************
3041  *           FindNextUrlCacheEntryA (WININET.@)
3042  */
3043 BOOL WINAPI FindNextUrlCacheEntryA(
3044   HANDLE hEnumHandle,
3045   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3046   LPDWORD lpdwNextCacheEntryInfoBufferSize)
3047 {
3048     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3049     URLCACHECONTAINER * pContainer;
3050
3051     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3052
3053     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3054     {
3055         SetLastError(ERROR_INVALID_HANDLE);
3056         return FALSE;
3057     }
3058
3059     for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3060          pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3061     {
3062         LPURLCACHE_HEADER pHeader;
3063         HASH_CACHEFILE_ENTRY *pHashTableEntry;
3064
3065         if (!URLCacheContainer_OpenIndex(pContainer))
3066             return FALSE;
3067
3068         if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3069             return FALSE;
3070
3071         for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3072              pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3073         {
3074             const struct _HASH_ENTRY *pHashEntry = NULL;
3075             for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3076                  pEntryHandle->dwHashEntryIndex++)
3077             {
3078                 const URL_CACHEFILE_ENTRY *pUrlEntry;
3079                 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3080
3081                 if (pEntry->dwSignature != URL_SIGNATURE)
3082                     continue;
3083
3084                 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3085                 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3086                 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3087
3088                 if (!URLCache_CopyEntry(
3089                     pContainer,
3090                     pHeader,
3091                     lpNextCacheEntryInfo,
3092                     lpdwNextCacheEntryInfoBufferSize,
3093                     pUrlEntry,
3094                     FALSE /* not UNICODE */))
3095                 {
3096                     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3097                     return FALSE;
3098                 }
3099                 TRACE("Local File Name: %s\n", debugstr_a(lpNextCacheEntryInfo->lpszLocalFileName));
3100
3101                 /* increment the current index so that next time the function
3102                  * is called the next entry is returned */
3103                 pEntryHandle->dwHashEntryIndex++;
3104                 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3105                 return TRUE;
3106             }
3107         }
3108
3109         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3110     }
3111
3112     SetLastError(ERROR_NO_MORE_ITEMS);
3113     return FALSE;
3114 }
3115
3116 BOOL WINAPI FindNextUrlCacheEntryW(
3117   HANDLE hEnumHandle,
3118   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3119   LPDWORD lpdwNextCacheEntryInfoBufferSize
3120 )
3121 {
3122     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3123     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3124     return FALSE;
3125 }
3126
3127 /***********************************************************************
3128  *           FindCloseUrlCache (WININET.@)
3129  */
3130 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3131 {
3132     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3133
3134     TRACE("(%p)\n", hEnumHandle);
3135
3136     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3137     {
3138         SetLastError(ERROR_INVALID_HANDLE);
3139         return FALSE;
3140     }
3141
3142     pEntryHandle->dwMagic = 0;
3143     HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3144     HeapFree(GetProcessHeap(), 0, pEntryHandle);
3145
3146     return TRUE;
3147 }
3148
3149 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3150                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3151 {
3152     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3153           dwSearchCondition, lpGroupId, lpReserved);
3154     return NULL;
3155 }
3156
3157 BOOL WINAPI FindNextUrlCacheEntryExA(
3158   HANDLE hEnumHandle,
3159   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3160   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3161   LPVOID lpReserved,
3162   LPDWORD pcbReserved2,
3163   LPVOID lpReserved3
3164 )
3165 {
3166     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3167           lpReserved, pcbReserved2, lpReserved3);
3168     return FALSE;
3169 }
3170
3171 BOOL WINAPI FindNextUrlCacheEntryExW(
3172   HANDLE hEnumHandle,
3173   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3174   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3175   LPVOID lpReserved,
3176   LPDWORD pcbReserved2,
3177   LPVOID lpReserved3
3178 )
3179 {
3180     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3181           lpReserved, pcbReserved2, lpReserved3);
3182     return FALSE;
3183 }
3184
3185 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3186 {
3187     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3188     return FALSE;
3189 }
3190
3191 /***********************************************************************
3192  *           CreateUrlCacheGroup (WININET.@)
3193  *
3194  */
3195 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3196 {
3197   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3198   return FALSE;
3199 }
3200
3201 /***********************************************************************
3202  *           DeleteUrlCacheGroup (WININET.@)
3203  *
3204  */
3205 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3206 {
3207     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3208           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3209     return FALSE;
3210 }
3211
3212 /***********************************************************************
3213  *           SetUrlCacheEntryGroupA (WININET.@)
3214  *
3215  */
3216 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3217   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3218   LPVOID lpReserved)
3219 {
3220     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3221           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3222           pbGroupAttributes, cbGroupAttributes, lpReserved);
3223     SetLastError(ERROR_FILE_NOT_FOUND);
3224     return FALSE;
3225 }
3226
3227 /***********************************************************************
3228  *           SetUrlCacheEntryGroupW (WININET.@)
3229  *
3230  */
3231 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3232   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3233   LPVOID lpReserved)
3234 {
3235     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3236           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3237           pbGroupAttributes, cbGroupAttributes, lpReserved);
3238     SetLastError(ERROR_FILE_NOT_FOUND);
3239     return FALSE;
3240 }
3241
3242 /***********************************************************************
3243  *           GetUrlCacheConfigInfoW (WININET.@)
3244  */
3245 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3246 {
3247     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3248     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3249     return FALSE;
3250 }
3251
3252 /***********************************************************************
3253  *           GetUrlCacheConfigInfoA (WININET.@)
3254  *
3255  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3256  */
3257 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3258 {
3259     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3260     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3261     return FALSE;
3262 }
3263
3264 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3265                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3266                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3267 {
3268     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3269           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3270           lpdwGroupInfo, lpReserved);
3271     return FALSE;
3272 }
3273
3274 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3275                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3276                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3277 {
3278     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3279           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3280           lpdwGroupInfo, lpReserved);
3281     return FALSE;
3282 }
3283
3284 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3285                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3286 {
3287     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3288           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3289     return TRUE;
3290 }
3291
3292 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3293                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3294 {
3295     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3296           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3297     return TRUE;
3298 }
3299
3300 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3301 {
3302     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3303     return TRUE;
3304 }
3305
3306 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3307 {
3308     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3309     return TRUE;
3310 }
3311
3312 /***********************************************************************
3313  *           DeleteIE3Cache (WININET.@)
3314  *
3315  * Deletes the files used by the IE3 URL caching system.
3316  *
3317  * PARAMS
3318  *   hWnd        [I] A dummy window.
3319  *   hInst       [I] Instance of process calling the function.
3320  *   lpszCmdLine [I] Options used by function.
3321  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
3322  */
3323 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3324 {
3325     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3326     return 0;
3327 }
3328
3329 /***********************************************************************
3330  *           IsUrlCacheEntryExpiredA (WININET.@)
3331  *
3332  * PARAMS
3333  *   url             [I] Url
3334  *   dwFlags         [I] Unknown
3335  *   pftLastModified [O] Last modified time
3336  */
3337 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3338 {
3339     LPURLCACHE_HEADER pHeader;
3340     struct _HASH_ENTRY * pHashEntry;
3341     const CACHEFILE_ENTRY * pEntry;
3342     const URL_CACHEFILE_ENTRY * pUrlEntry;
3343     URLCACHECONTAINER * pContainer;
3344
3345     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3346
3347     if (!URLCacheContainers_FindContainerA(url, &pContainer))
3348         return FALSE;
3349
3350     if (!URLCacheContainer_OpenIndex(pContainer))
3351         return FALSE;
3352
3353     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3354         return FALSE;
3355
3356     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3357     {
3358         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3359         TRACE("entry %s not found!\n", url);
3360         SetLastError(ERROR_FILE_NOT_FOUND);
3361         return FALSE;
3362     }
3363
3364     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3365     if (pEntry->dwSignature != URL_SIGNATURE)
3366     {
3367         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3368         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3369         SetLastError(ERROR_FILE_NOT_FOUND);
3370         return FALSE;
3371     }
3372
3373     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3374
3375     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3376
3377     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3378
3379     return TRUE;
3380 }
3381
3382 /***********************************************************************
3383  *           IsUrlCacheEntryExpiredW (WININET.@)
3384  *
3385  * PARAMS
3386  *   url             [I] Url
3387  *   dwFlags         [I] Unknown
3388  *   pftLastModified [O] Last modified time
3389  */
3390 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3391 {
3392     LPURLCACHE_HEADER pHeader;
3393     struct _HASH_ENTRY * pHashEntry;
3394     const CACHEFILE_ENTRY * pEntry;
3395     const URL_CACHEFILE_ENTRY * pUrlEntry;
3396     URLCACHECONTAINER * pContainer;
3397
3398     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3399
3400     if (!URLCacheContainers_FindContainerW(url, &pContainer))
3401         return FALSE;
3402
3403     if (!URLCacheContainer_OpenIndex(pContainer))
3404         return FALSE;
3405
3406     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3407         return FALSE;
3408
3409     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3410     {
3411         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3412         TRACE("entry %s not found!\n", debugstr_w(url));
3413         SetLastError(ERROR_FILE_NOT_FOUND);
3414         return FALSE;
3415     }
3416
3417     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3418     if (pEntry->dwSignature != URL_SIGNATURE)
3419     {
3420         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3421         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3422         SetLastError(ERROR_FILE_NOT_FOUND);
3423         return FALSE;
3424     }
3425
3426     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3427
3428     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3429
3430     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3431
3432     return TRUE;
3433 }