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