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