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