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