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