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