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