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