server: Avoid refreshing areas of a window that have already been copied by the X...
[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 exluding 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; /* hande 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 (FAILED(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     struct list * cursor;
587
588     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
589
590     LIST_FOR_EACH(cursor, &UrlContainers)
591     {
592         URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
593         int prefix_len = strlenW(pContainer->cache_prefix);
594         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
595         {
596             TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
597             *ppContainer = pContainer;
598             return TRUE;
599         }
600     }
601     ERR("no container found\n");
602     SetLastError(ERROR_FILE_NOT_FOUND);
603     return FALSE;
604 }
605
606 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
607 {
608     BOOL ret;
609     LPWSTR lpwszUrl;
610     int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
611     if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
612     {
613         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
614         ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
615         HeapFree(GetProcessHeap(), 0, lpwszUrl);
616         return ret;
617     }
618     return FALSE;
619 }
620
621 /***********************************************************************
622  *           URLCacheContainer_LockIndex (Internal)
623  *
624  */
625 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
626 {
627     BYTE index;
628     LPVOID pIndexData;
629     URLCACHE_HEADER * pHeader;
630
631     /* acquire mutex */
632     WaitForSingleObject(pContainer->hMutex, INFINITE);
633
634     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
635
636     if (!pIndexData)
637     {
638         ReleaseMutex(pContainer->hMutex);
639         ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
640         return FALSE;
641     }
642     pHeader = (URLCACHE_HEADER *)pIndexData;
643
644     /* file has grown - we need to remap to prevent us getting
645      * access violations when we try and access beyond the end
646      * of the memory mapped file */
647     if (pHeader->dwFileSize != pContainer->file_size)
648     {
649         URLCacheContainer_CloseIndex(pContainer);
650         if (!URLCacheContainer_OpenIndex(pContainer))
651         {
652             ReleaseMutex(pContainer->hMutex);
653             return FALSE;
654         }
655         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
656
657         if (!pIndexData)
658         {
659             ReleaseMutex(pContainer->hMutex);
660             ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
661             return FALSE;
662         }
663         pHeader = (URLCACHE_HEADER *)pIndexData;
664     }
665
666     TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
667
668     for (index = 0; index < pHeader->DirectoryCount; index++)
669     {
670         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
671     }
672     
673     return pHeader;
674 }
675
676 /***********************************************************************
677  *           URLCacheContainer_UnlockIndex (Internal)
678  *
679  */
680 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
681 {
682     /* release mutex */
683     ReleaseMutex(pContainer->hMutex);
684     return UnmapViewOfFile(pHeader);
685 }
686
687
688 #ifndef CHAR_BIT
689 #define CHAR_BIT    (8 * sizeof(CHAR))
690 #endif
691
692 /***********************************************************************
693  *           URLCache_Allocation_BlockIsFree (Internal)
694  *
695  *  Is the specified block number free?
696  *
697  * RETURNS
698  *    zero if free
699  *    non-zero otherwise
700  *
701  */
702 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
703 {
704     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
705     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
706 }
707
708 /***********************************************************************
709  *           URLCache_Allocation_BlockFree (Internal)
710  *
711  *  Marks the specified block as free
712  *
713  * RETURNS
714  *    nothing
715  *
716  */
717 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
718 {
719     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
720     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
721 }
722
723 /***********************************************************************
724  *           URLCache_Allocation_BlockAllocate (Internal)
725  *
726  *  Marks the specified block as allocated
727  *
728  * RETURNS
729  *    nothing
730  *
731  */
732 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
733 {
734     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
735     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
736 }
737
738 /***********************************************************************
739  *           URLCache_FindFirstFreeEntry (Internal)
740  *
741  *  Finds and allocates the first block of free space big enough and
742  * sets ppEntry to point to it.
743  *
744  * RETURNS
745  *    TRUE if it had enough space
746  *    FALSE if it couldn't find enough space
747  *
748  */
749 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
750 {
751     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
752     DWORD dwBlockNumber;
753     DWORD dwFreeCounter;
754     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
755     {
756         for (dwFreeCounter = 0; 
757             dwFreeCounter < dwBlocksNeeded &&
758               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
759               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
760             dwFreeCounter++)
761                 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
762
763         if (dwFreeCounter == dwBlocksNeeded)
764         {
765             DWORD index;
766             TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
767             for (index = 0; index < dwBlocksNeeded; index++)
768                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
769             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
770             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
771             return TRUE;
772         }
773     }
774     FIXME("Grow file\n");
775     return FALSE;
776 }
777
778 /***********************************************************************
779  *           URLCache_DeleteEntry (Internal)
780  *
781  *  Deletes the specified entry and frees the space allocated to it
782  *
783  * RETURNS
784  *    TRUE if it succeeded
785  *    FALSE if it failed
786  *
787  */
788 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
789 {
790     DWORD dwStartBlock;
791     DWORD dwBlock;
792     BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
793
794     /* update allocation table */
795     dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
796     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
797         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
798
799     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
800     return TRUE;
801 }
802
803 /***********************************************************************
804  *           URLCache_LocalFileNameToPathW (Internal)
805  *
806  *  Copies the full path to the specified buffer given the local file
807  * name and the index of the directory it is in. Always sets value in
808  * lpBufferSize to the required buffer size (in bytes).
809  *
810  * RETURNS
811  *    TRUE if the buffer was big enough
812  *    FALSE if the buffer was too small
813  *
814  */
815 static BOOL URLCache_LocalFileNameToPathW(
816     const URLCACHECONTAINER * pContainer,
817     LPCURLCACHE_HEADER pHeader,
818     LPCSTR szLocalFileName,
819     BYTE Directory,
820     LPWSTR wszPath,
821     LPLONG lpBufferSize)
822 {
823     LONG nRequired;
824     int path_len = strlenW(pContainer->path);
825     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
826     if (Directory >= pHeader->DirectoryCount)
827     {
828         *lpBufferSize = 0;
829         return FALSE;
830     }
831
832     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
833     if (nRequired < *lpBufferSize)
834     {
835         int dir_len;
836
837         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
838         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
839         wszPath[dir_len + path_len] = '\\';
840         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
841         *lpBufferSize = nRequired;
842         return TRUE;
843     }
844     *lpBufferSize = nRequired;
845     return FALSE;
846 }
847
848 /***********************************************************************
849  *           URLCache_LocalFileNameToPathA (Internal)
850  *
851  *  Copies the full path to the specified buffer given the local file
852  * name and the index of the directory it is in. Always sets value in
853  * lpBufferSize to the required buffer size.
854  *
855  * RETURNS
856  *    TRUE if the buffer was big enough
857  *    FALSE if the buffer was too small
858  *
859  */
860 static BOOL URLCache_LocalFileNameToPathA(
861     const URLCACHECONTAINER * pContainer,
862     LPCURLCACHE_HEADER pHeader,
863     LPCSTR szLocalFileName,
864     BYTE Directory,
865     LPSTR szPath,
866     LPLONG lpBufferSize)
867 {
868     LONG nRequired;
869     int path_len, file_name_len, dir_len;
870
871     if (Directory >= pHeader->DirectoryCount)
872     {
873         *lpBufferSize = 0;
874         return FALSE;
875     }
876
877     path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
878     file_name_len = strlen(szLocalFileName);
879     dir_len = DIR_LENGTH;
880
881     nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(WCHAR);
882     if (nRequired < *lpBufferSize)
883     {
884         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
885         memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
886         szPath[path_len + dir_len] = '\\';
887         memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
888         *lpBufferSize = nRequired;
889         return TRUE;
890     }
891     *lpBufferSize = nRequired;
892     return FALSE;
893 }
894
895 /***********************************************************************
896  *           URLCache_CopyEntry (Internal)
897  *
898  *  Copies an entry from the cache index file to the Win32 structure
899  *
900  * RETURNS
901  *    TRUE if the buffer was big enough
902  *    FALSE if the buffer was too small
903  *
904  */
905 static BOOL URLCache_CopyEntry(
906     URLCACHECONTAINER * pContainer,
907     LPCURLCACHE_HEADER pHeader, 
908     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
909     LPDWORD lpdwBufferSize, 
910     URL_CACHEFILE_ENTRY * pUrlEntry,
911     BOOL bUnicode)
912 {
913     int lenUrl;
914     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
915
916     if (*lpdwBufferSize >= dwRequiredSize)
917     {
918         lpCacheEntryInfo->lpHeaderInfo = NULL;
919         lpCacheEntryInfo->lpszFileExtension = NULL;
920         lpCacheEntryInfo->lpszLocalFileName = NULL;
921         lpCacheEntryInfo->lpszSourceUrlName = NULL;
922         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
923         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
924         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
925         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
926         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
927         lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
928         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
929         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
930         DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
931         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
932         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
933         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
934         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
935         DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
936     }
937
938     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
939         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
940     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
941     if (bUnicode)
942         lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
943     else
944         lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
945     dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
946
947     /* FIXME: is source url optional? */
948     if (*lpdwBufferSize >= dwRequiredSize)
949     {
950         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
951         if (bUnicode)
952             MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
953         else
954             memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
955     }
956
957     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
958         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
959     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
960
961     if (pUrlEntry->dwOffsetLocalName)
962     {
963         LONG nLocalFilePathSize;
964         LPSTR lpszLocalFileName;
965         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
966         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
967         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
968             URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
969         {
970             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
971         }
972         dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
973
974         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
975             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
976         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
977     }
978     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
979
980     if (*lpdwBufferSize >= dwRequiredSize)
981     {
982         lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
983         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
984         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
985     }
986     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
987         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
988     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
989
990     if (dwRequiredSize > *lpdwBufferSize)
991     {
992         *lpdwBufferSize = dwRequiredSize;
993         SetLastError(ERROR_INSUFFICIENT_BUFFER);
994         return FALSE;
995     }
996     *lpdwBufferSize = dwRequiredSize;
997     return TRUE;
998 }
999
1000
1001 /***********************************************************************
1002  *           URLCache_SetEntryInfo (Internal)
1003  *
1004  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1005  * according the the flags set by dwFieldControl.
1006  *
1007  * RETURNS
1008  *    TRUE if the buffer was big enough
1009  *    FALSE if the buffer was too small
1010  *
1011  */
1012 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1013 {
1014     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1015         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1016     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1017         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1018     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1019         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1020     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1021         FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1022     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1023         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1024     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1025         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1026     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1027         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1028     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1029         FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1030
1031     return TRUE;
1032 }
1033
1034 /***********************************************************************
1035  *           URLCache_HashKey (Internal)
1036  *
1037  *  Returns the hash key for a given string
1038  *
1039  * RETURNS
1040  *    hash key for the string
1041  *
1042  */
1043 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1044 {
1045     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1046      * but the algorithm and result are not the same!
1047      */
1048     static const unsigned char lookupTable[256] = 
1049     {
1050         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1051         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1052         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1053         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1054         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1055         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1056         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1057         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1058         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1059         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1060         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1061         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1062         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1063         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1064         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1065         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1066         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1067         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1068         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1069         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1070         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1071         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1072         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1073         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1074         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1075         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1076         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1077         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1078         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1079         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1080         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1081         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1082     };
1083     BYTE key[4];
1084     DWORD i;
1085     int subscript[sizeof(key) / sizeof(key[0])];
1086
1087     subscript[0] = *lpszKey;
1088     subscript[1] = (char)(*lpszKey + 1);
1089     subscript[2] = (char)(*lpszKey + 2);
1090     subscript[3] = (char)(*lpszKey + 3);
1091
1092     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1093         key[i] = lookupTable[i];
1094
1095     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1096     {
1097         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1098             key[i] = lookupTable[*lpszKey ^ key[i]];
1099     }
1100
1101     return *(DWORD *)key;
1102 }
1103
1104 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1105 {
1106     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1107 }
1108
1109 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1110 {
1111     /* structure of hash table:
1112      *  448 entries divided into 64 blocks
1113      *  each block therefore contains a chain of 7 key/offset pairs
1114      * how position in table is calculated:
1115      *  1. the url is hashed in helper function
1116      *  2. the key % 64 * 8 is the offset
1117      *  3. the key in the hash table is the hash key aligned to 64
1118      *
1119      * note:
1120      *  there can be multiple hash tables in the file and the offset to
1121      *  the next one is stored in the header of the hash table
1122      */
1123     DWORD key = URLCache_HashKey(lpszUrl);
1124     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1125     HASH_CACHEFILE_ENTRY * pHashEntry;
1126     DWORD dwHashTableNumber = 0;
1127
1128     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1129
1130     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1131          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1132          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1133     {
1134         int i;
1135         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1136         {
1137             ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1138             continue;
1139         }
1140         /* make sure that it is in fact a hash entry */
1141         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1142         {
1143             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1144             continue;
1145         }
1146
1147         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1148         {
1149             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1150             if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1151             {
1152                 /* FIXME: we should make sure that this is the right element
1153                  * before returning and claiming that it is. We can do this
1154                  * by doing a simple compare between the URL we were given
1155                  * and the URL stored in the entry. However, this assumes
1156                  * we know the format of all the entries stored in the
1157                  * hash table */
1158                 *ppHashEntry = pHashElement;
1159                 return TRUE;
1160             }
1161         }
1162     }
1163     return FALSE;
1164 }
1165
1166 /***********************************************************************
1167  *           URLCache_FindEntryInHash (Internal)
1168  *
1169  *  Searches all the hash tables in the index for the given URL and
1170  * returns the entry, if it was found, in ppEntry
1171  *
1172  * RETURNS
1173  *    TRUE if the entry was found
1174  *    FALSE if the entry could not be found
1175  *
1176  */
1177 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
1178 {
1179     struct _HASH_ENTRY * pHashEntry;
1180     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1181     {
1182         *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1183         return TRUE;
1184     }
1185     return FALSE;
1186 }
1187
1188 /***********************************************************************
1189  *           URLCache_HashEntrySetUse (Internal)
1190  *
1191  *  Searches all the hash tables in the index for the given URL and
1192  * sets the use count (stored or'ed with key)
1193  *
1194  * RETURNS
1195  *    TRUE if the entry was found
1196  *    FALSE if the entry could not be found
1197  *
1198  */
1199 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1200 {
1201     struct _HASH_ENTRY * pHashEntry;
1202     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1203     {
1204         pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1205         return TRUE;
1206     }
1207     return FALSE;
1208 }
1209
1210 /***********************************************************************
1211  *           URLCache_DeleteEntryFromHash (Internal)
1212  *
1213  *  Searches all the hash tables in the index for the given URL and
1214  * then if found deletes the entry.
1215  *
1216  * RETURNS
1217  *    TRUE if the entry was found
1218  *    FALSE if the entry could not be found
1219  *
1220  */
1221 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1222 {
1223     struct _HASH_ENTRY * pHashEntry;
1224     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1225     {
1226         pHashEntry->dwHashKey = HASHTABLE_FREE;
1227         pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1228         return TRUE;
1229     }
1230     return FALSE;
1231 }
1232
1233 /***********************************************************************
1234  *           URLCache_AddEntryToHash (Internal)
1235  *
1236  *  Searches all the hash tables for a free slot based on the offset
1237  * generated from the hash key. If a free slot is found, the offset and
1238  * key are entered into the hash table.
1239  *
1240  * RETURNS
1241  *    TRUE if the entry was added
1242  *    FALSE if the entry could not be added
1243  *
1244  */
1245 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1246 {
1247     /* see URLCache_FindEntryInHash for structure of hash tables */
1248
1249     DWORD key = URLCache_HashKey(lpszUrl);
1250     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1251     HASH_CACHEFILE_ENTRY * pHashEntry;
1252     DWORD dwHashTableNumber = 0;
1253
1254     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1255
1256     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1257          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1258          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1259     {
1260         int i;
1261         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1262         {
1263             ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1264             break;
1265         }
1266         /* make sure that it is in fact a hash entry */
1267         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1268         {
1269             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1270             break;
1271         }
1272
1273         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1274         {
1275             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1276             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1277             {
1278                 pHashElement->dwHashKey = key;
1279                 pHashElement->dwOffsetEntry = dwOffsetEntry;
1280                 return TRUE;
1281             }
1282         }
1283     }
1284     pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1285     if (!pHashEntry)
1286         return FALSE;
1287
1288     pHashEntry->HashTable[offset].dwHashKey = key;
1289     pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1290     return TRUE;
1291 }
1292
1293 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1294 {
1295     HASH_CACHEFILE_ENTRY *pHash;
1296     DWORD dwOffset;
1297     int i;
1298
1299     if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1300     {
1301         FIXME("no free space for hash table\n");
1302         SetLastError(ERROR_DISK_FULL);
1303         return NULL;
1304     }
1305
1306     dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1307
1308     if (pPrevHash)
1309         pPrevHash->dwAddressNext = dwOffset;
1310     else
1311         pHeader->dwOffsetFirstHashTable = dwOffset;
1312     pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1313     pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1314     pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1315     for (i = 0; i < HASHTABLE_SIZE; i++)
1316     {
1317         pHash->HashTable[i].dwOffsetEntry = 0;
1318         pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1319     }
1320     return pHash;
1321 }
1322
1323 /***********************************************************************
1324  *           GetUrlCacheEntryInfoExA (WININET.@)
1325  *
1326  */
1327 BOOL WINAPI GetUrlCacheEntryInfoExA(
1328     LPCSTR lpszUrl,
1329     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1330     LPDWORD lpdwCacheEntryInfoBufSize,
1331     LPSTR lpszReserved,
1332     LPDWORD lpdwReserved,
1333     LPVOID lpReserved,
1334     DWORD dwFlags)
1335 {
1336     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1337         debugstr_a(lpszUrl), 
1338         lpCacheEntryInfo,
1339         lpdwCacheEntryInfoBufSize,
1340         lpszReserved,
1341         lpdwReserved,
1342         lpReserved,
1343         dwFlags);
1344
1345     if ((lpszReserved != NULL) ||
1346         (lpdwReserved != NULL) ||
1347         (lpReserved != NULL))
1348     {
1349         ERR("Reserved value was not 0\n");
1350         SetLastError(ERROR_INVALID_PARAMETER);
1351         return FALSE;
1352     }
1353     if (dwFlags != 0)
1354         FIXME("Undocumented flag(s): %x\n", dwFlags);
1355     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1356 }
1357
1358 /***********************************************************************
1359  *           GetUrlCacheEntryInfoA (WININET.@)
1360  *
1361  */
1362 BOOL WINAPI GetUrlCacheEntryInfoA(
1363     IN LPCSTR lpszUrlName,
1364     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1365     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1366 )
1367 {
1368     LPURLCACHE_HEADER pHeader;
1369     CACHEFILE_ENTRY * pEntry;
1370     URL_CACHEFILE_ENTRY * pUrlEntry;
1371     URLCACHECONTAINER * pContainer;
1372
1373     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1374
1375     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1376         return FALSE;
1377
1378     if (!URLCacheContainer_OpenIndex(pContainer))
1379         return FALSE;
1380
1381     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1382         return FALSE;
1383
1384     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1385     {
1386         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1387         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1388         SetLastError(ERROR_FILE_NOT_FOUND);
1389         return FALSE;
1390     }
1391
1392     if (pEntry->dwSignature != URL_SIGNATURE)
1393     {
1394         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1395         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1396         SetLastError(ERROR_FILE_NOT_FOUND);
1397         return FALSE;
1398     }
1399
1400     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1401     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1402     if (pUrlEntry->dwOffsetHeaderInfo)
1403         TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1404
1405     if (!URLCache_CopyEntry(
1406         pContainer,
1407         pHeader,
1408         lpCacheEntryInfo,
1409         lpdwCacheEntryInfoBufferSize,
1410         pUrlEntry,
1411         FALSE /* ANSI */))
1412     {
1413         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1414         return FALSE;
1415     }
1416     TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1417
1418     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1419
1420     return TRUE;
1421 }
1422
1423 /***********************************************************************
1424  *           GetUrlCacheEntryInfoW (WININET.@)
1425  *
1426  */
1427 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1428   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1429   LPDWORD lpdwCacheEntryInfoBufferSize)
1430 {
1431     LPURLCACHE_HEADER pHeader;
1432     CACHEFILE_ENTRY * pEntry;
1433     URL_CACHEFILE_ENTRY * pUrlEntry;
1434     URLCACHECONTAINER * pContainer;
1435     LPSTR lpszUrlA;
1436     int url_len;
1437
1438     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1439
1440     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1441     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1442     if (!lpszUrlA)
1443     {
1444         SetLastError(ERROR_OUTOFMEMORY);
1445         return FALSE;
1446     }
1447     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1448
1449     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1450     {
1451         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1452         return FALSE;
1453     }
1454
1455     if (!URLCacheContainer_OpenIndex(pContainer))
1456     {
1457         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1458         return FALSE;
1459     }
1460
1461     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1462     {
1463         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1464         return FALSE;
1465     }
1466
1467     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1468     {
1469         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1470         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1471         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1472         SetLastError(ERROR_FILE_NOT_FOUND);
1473         return FALSE;
1474     }
1475     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1476
1477     if (pEntry->dwSignature != URL_SIGNATURE)
1478     {
1479         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1480         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1481         SetLastError(ERROR_FILE_NOT_FOUND);
1482         return FALSE;
1483     }
1484
1485     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1486     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1487     TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1488
1489     if (!URLCache_CopyEntry(
1490         pContainer,
1491         pHeader,
1492         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1493         lpdwCacheEntryInfoBufferSize,
1494         pUrlEntry,
1495         TRUE /* UNICODE */))
1496     {
1497         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1498         return FALSE;
1499     }
1500     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1501
1502     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1503
1504     return TRUE;
1505 }
1506
1507 /***********************************************************************
1508  *           GetUrlCacheEntryInfoExW (WININET.@)
1509  *
1510  */
1511 BOOL WINAPI GetUrlCacheEntryInfoExW(
1512     LPCWSTR lpszUrl,
1513     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1514     LPDWORD lpdwCacheEntryInfoBufSize,
1515     LPWSTR lpszReserved,
1516     LPDWORD lpdwReserved,
1517     LPVOID lpReserved,
1518     DWORD dwFlags)
1519 {
1520     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1521         debugstr_w(lpszUrl), 
1522         lpCacheEntryInfo,
1523         lpdwCacheEntryInfoBufSize,
1524         lpszReserved,
1525         lpdwReserved,
1526         lpReserved,
1527         dwFlags);
1528
1529     if ((lpszReserved != NULL) ||
1530         (lpdwReserved != NULL) ||
1531         (lpReserved != NULL))
1532     {
1533         ERR("Reserved value was not 0\n");
1534         SetLastError(ERROR_INVALID_PARAMETER);
1535         return FALSE;
1536     }
1537     if (dwFlags != 0)
1538         FIXME("Undocumented flag(s): %x\n", dwFlags);
1539     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1540 }
1541
1542 /***********************************************************************
1543  *           SetUrlCacheEntryInfoA (WININET.@)
1544  */
1545 BOOL WINAPI SetUrlCacheEntryInfoA(
1546     LPCSTR lpszUrlName,
1547     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1548     DWORD dwFieldControl)
1549 {
1550     LPURLCACHE_HEADER pHeader;
1551     CACHEFILE_ENTRY * pEntry;
1552     URLCACHECONTAINER * pContainer;
1553
1554     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1555
1556     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1557         return FALSE;
1558
1559     if (!URLCacheContainer_OpenIndex(pContainer))
1560         return FALSE;
1561
1562     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1563         return FALSE;
1564
1565     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1566     {
1567         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1568         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1569         SetLastError(ERROR_FILE_NOT_FOUND);
1570         return FALSE;
1571     }
1572
1573     if (pEntry->dwSignature != URL_SIGNATURE)
1574     {
1575         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1576         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1577         SetLastError(ERROR_FILE_NOT_FOUND);
1578         return FALSE;
1579     }
1580
1581     URLCache_SetEntryInfo(
1582         (URL_CACHEFILE_ENTRY *)pEntry,
1583         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1584         dwFieldControl);
1585
1586     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1587
1588     return TRUE;
1589 }
1590
1591 /***********************************************************************
1592  *           SetUrlCacheEntryInfoW (WININET.@)
1593  */
1594 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1595 {
1596     LPURLCACHE_HEADER pHeader;
1597     CACHEFILE_ENTRY * pEntry;
1598     URLCACHECONTAINER * pContainer;
1599     LPSTR lpszUrlA;
1600     int url_len;
1601
1602     TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1603
1604     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1605     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1606     if (!lpszUrlA)
1607     {
1608         SetLastError(ERROR_OUTOFMEMORY);
1609         return FALSE;
1610     }
1611     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1612
1613     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1614     {
1615         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1616         return FALSE;
1617     }
1618
1619     if (!URLCacheContainer_OpenIndex(pContainer))
1620     {
1621         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1622         return FALSE;
1623     }
1624
1625     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1626     {
1627         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1628         return FALSE;
1629     }
1630
1631     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1632     {
1633         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1634         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1635         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1636         SetLastError(ERROR_FILE_NOT_FOUND);
1637         return FALSE;
1638     }
1639     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1640
1641     if (pEntry->dwSignature != URL_SIGNATURE)
1642     {
1643         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1644         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1645         SetLastError(ERROR_FILE_NOT_FOUND);
1646         return FALSE;
1647     }
1648
1649     URLCache_SetEntryInfo(
1650         (URL_CACHEFILE_ENTRY *)pEntry,
1651         lpCacheEntryInfo,
1652         dwFieldControl);
1653
1654     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1655
1656     return TRUE;
1657 }
1658
1659 /***********************************************************************
1660  *           RetrieveUrlCacheEntryFileA (WININET.@)
1661  *
1662  */
1663 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1664     IN LPCSTR lpszUrlName,
1665     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1666     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1667     IN DWORD dwReserved
1668     )
1669 {
1670     LPURLCACHE_HEADER pHeader;
1671     CACHEFILE_ENTRY * pEntry;
1672     URL_CACHEFILE_ENTRY * pUrlEntry;
1673     URLCACHECONTAINER * pContainer;
1674
1675     TRACE("(%s, %p, %p, 0x%08x)\n",
1676         debugstr_a(lpszUrlName),
1677         lpCacheEntryInfo,
1678         lpdwCacheEntryInfoBufferSize,
1679         dwReserved);
1680
1681     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1682         return FALSE;
1683
1684     if (!URLCacheContainer_OpenIndex(pContainer))
1685         return FALSE;
1686
1687     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1688         return FALSE;
1689
1690     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1691     {
1692         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1693         TRACE("entry %s not found!\n", lpszUrlName);
1694         SetLastError(ERROR_FILE_NOT_FOUND);
1695         return FALSE;
1696     }
1697
1698     if (pEntry->dwSignature != URL_SIGNATURE)
1699     {
1700         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1701         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1702         SetLastError(ERROR_FILE_NOT_FOUND);
1703         return FALSE;
1704     }
1705
1706     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1707     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1708     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1709
1710     pUrlEntry->dwHitRate++;
1711     pUrlEntry->dwUseCount++;
1712     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1713
1714     if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1715     {
1716         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1717         return FALSE;
1718     }
1719     TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1720
1721     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1722
1723     return TRUE;
1724 }
1725
1726 /***********************************************************************
1727  *           RetrieveUrlCacheEntryFileW (WININET.@)
1728  *
1729  */
1730 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1731     IN LPCWSTR lpszUrlName,
1732     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1733     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1734     IN DWORD dwReserved
1735     )
1736 {
1737     TRACE("(%s, %p, %p, 0x%08x)\n",
1738         debugstr_w(lpszUrlName),
1739         lpCacheEntryInfo,
1740         lpdwCacheEntryInfoBufferSize,
1741         dwReserved);
1742
1743     return FALSE;
1744 }
1745
1746 /***********************************************************************
1747  *           UnlockUrlCacheEntryFileA (WININET.@)
1748  *
1749  */
1750 BOOL WINAPI UnlockUrlCacheEntryFileA(
1751     IN LPCSTR lpszUrlName, 
1752     IN DWORD dwReserved
1753     )
1754 {
1755     LPURLCACHE_HEADER pHeader;
1756     CACHEFILE_ENTRY * pEntry;
1757     URL_CACHEFILE_ENTRY * pUrlEntry;
1758     URLCACHECONTAINER * pContainer;
1759
1760     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1761
1762     if (dwReserved)
1763     {
1764         ERR("dwReserved != 0\n");
1765         SetLastError(ERROR_INVALID_PARAMETER);
1766         return FALSE;
1767     }
1768
1769     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1770        return FALSE;
1771
1772     if (!URLCacheContainer_OpenIndex(pContainer))
1773         return FALSE;
1774
1775     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1776         return FALSE;
1777
1778     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1779     {
1780         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1781         TRACE("entry %s not found!\n", lpszUrlName);
1782         SetLastError(ERROR_FILE_NOT_FOUND);
1783         return FALSE;
1784     }
1785
1786     if (pEntry->dwSignature != URL_SIGNATURE)
1787     {
1788         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1789         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1790         SetLastError(ERROR_FILE_NOT_FOUND);
1791         return FALSE;
1792     }
1793
1794     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1795
1796     if (pUrlEntry->dwUseCount == 0)
1797     {
1798         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1799         return FALSE;
1800     }
1801     pUrlEntry->dwUseCount--;
1802     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1803
1804     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1805
1806     return TRUE;
1807 }
1808
1809 /***********************************************************************
1810  *           UnlockUrlCacheEntryFileW (WININET.@)
1811  *
1812  */
1813 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1814 {
1815     FIXME("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1816     return TRUE;
1817 }
1818
1819 /***********************************************************************
1820  *           CreateUrlCacheEntryA (WININET.@)
1821  *
1822  */
1823 BOOL WINAPI CreateUrlCacheEntryA(
1824     IN LPCSTR lpszUrlName,
1825     IN DWORD dwExpectedFileSize,
1826     IN LPCSTR lpszFileExtension,
1827     OUT LPSTR lpszFileName,
1828     IN DWORD dwReserved
1829 )
1830 {
1831     DWORD len;
1832     WCHAR *url_name;
1833     WCHAR *file_extension;
1834     WCHAR file_name[MAX_PATH];
1835     BOOL bSuccess = FALSE;
1836     DWORD dwError = 0;
1837
1838     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
1839         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1840     {
1841         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
1842         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
1843             (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1844         {
1845             MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
1846             if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
1847             {
1848                 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
1849                 {
1850                     bSuccess = TRUE;
1851                 }
1852                 else
1853                 {
1854                     dwError = GetLastError();
1855                 }
1856             }
1857             else
1858             {
1859                 dwError = GetLastError();
1860             }
1861             HeapFree(GetProcessHeap(), 0, file_extension);
1862         }
1863         else
1864         {
1865             dwError = GetLastError();
1866         }
1867         HeapFree(GetProcessHeap(), 0, url_name);
1868         if (!bSuccess)
1869             SetLastError(dwError);
1870     }
1871     return bSuccess;
1872 }
1873 /***********************************************************************
1874  *           CreateUrlCacheEntryW (WININET.@)
1875  *
1876  */
1877 BOOL WINAPI CreateUrlCacheEntryW(
1878     IN LPCWSTR lpszUrlName,
1879     IN DWORD dwExpectedFileSize,
1880     IN LPCWSTR lpszFileExtension,
1881     OUT LPWSTR lpszFileName,
1882     IN DWORD dwReserved
1883 )
1884 {
1885     URLCACHECONTAINER * pContainer;
1886     LPURLCACHE_HEADER pHeader;
1887     CHAR szFile[MAX_PATH];
1888     WCHAR szExtension[MAX_PATH];
1889     LPCWSTR lpszUrlPart;
1890     LPCWSTR lpszUrlEnd;
1891     LPCWSTR lpszFileNameExtension;
1892     LPWSTR lpszFileNameNoPath;
1893     int i;
1894     int countnoextension;
1895     BYTE CacheDir;
1896     LONG lBufferSize;
1897     BOOL bFound = FALSE;
1898     int count;
1899     static const WCHAR szWWW[] = {'w','w','w',0};
1900
1901     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
1902         debugstr_w(lpszUrlName),
1903         dwExpectedFileSize,
1904         debugstr_w(lpszFileExtension),
1905         lpszFileName,
1906         dwReserved);
1907
1908     if (dwReserved)
1909     {
1910         ERR("dwReserved != 0\n");
1911         SetLastError(ERROR_INVALID_PARAMETER);
1912         return FALSE;
1913     }
1914
1915     for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1916         ;
1917     
1918     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1919         lpszUrlEnd--;
1920
1921     for (lpszUrlPart = lpszUrlEnd; 
1922         (lpszUrlPart >= lpszUrlName); 
1923         lpszUrlPart--)
1924     {
1925         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1926         {
1927             bFound = TRUE;
1928             lpszUrlPart++;
1929             break;
1930         }
1931     }
1932     if (!lstrcmpW(lpszUrlPart, szWWW))
1933     {
1934         lpszUrlPart += lstrlenW(szWWW);
1935     }
1936
1937     count = lpszUrlEnd - lpszUrlPart;
1938
1939     if (bFound && (count < MAX_PATH))
1940     {
1941         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
1942         if (!len)
1943             return FALSE;
1944         szFile[len] = '\0';
1945         /* FIXME: get rid of illegal characters like \, / and : */
1946     }
1947     else
1948     {
1949         FIXME("need to generate a random filename\n");
1950     }
1951
1952     TRACE("File name: %s\n", debugstr_a(szFile));
1953
1954     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1955         return FALSE;
1956
1957     if (!URLCacheContainer_OpenIndex(pContainer))
1958         return FALSE;
1959
1960     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1961         return FALSE;
1962
1963     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1964
1965     lBufferSize = MAX_PATH * sizeof(WCHAR);
1966     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1967
1968     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1969
1970     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
1971         lpszFileNameNoPath >= lpszFileName; 
1972         --lpszFileNameNoPath)
1973     {
1974         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
1975             break;
1976     }
1977
1978     countnoextension = lstrlenW(lpszFileNameNoPath);
1979     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
1980     if (lpszFileNameExtension)
1981         countnoextension -= lstrlenW(lpszFileNameExtension);
1982     *szExtension = '\0';
1983
1984     if (lpszFileExtension)
1985     {
1986         szExtension[0] = '.';
1987         lstrcpyW(szExtension+1, lpszFileExtension);
1988     }
1989
1990     for (i = 0; i < 255; i++)
1991     {
1992         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
1993         HANDLE hFile;
1994         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
1995         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
1996         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1997         if (hFile != INVALID_HANDLE_VALUE)
1998         {
1999             CloseHandle(hFile);
2000             return TRUE;
2001         }
2002     }
2003
2004     return FALSE;
2005 }
2006
2007
2008 /***********************************************************************
2009  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2010  *
2011  *   The bug we are compensating for is that some drongo at Microsoft
2012  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2013  *   As a consequence, CommitUrlCacheEntryA has been effectively
2014  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2015  *   is still defined as LPCWSTR. The result (other than madness) is
2016  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2017  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2018  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2019  *   result will lose data for arbitrary binary data.
2020  *
2021  */
2022 static BOOL WINAPI CommitUrlCacheEntryInternal(
2023     IN LPCWSTR lpszUrlName,
2024     IN LPCWSTR lpszLocalFileName,
2025     IN FILETIME ExpireTime,
2026     IN FILETIME LastModifiedTime,
2027     IN DWORD CacheEntryType,
2028     IN LPBYTE lpHeaderInfo,
2029     IN DWORD dwHeaderSize,
2030     IN LPCWSTR lpszFileExtension,
2031     IN LPCWSTR lpszOriginalUrl
2032     )
2033 {
2034     URLCACHECONTAINER * pContainer;
2035     LPURLCACHE_HEADER pHeader;
2036     CACHEFILE_ENTRY * pEntry;
2037     URL_CACHEFILE_ENTRY * pUrlEntry;
2038     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2039     DWORD dwOffsetLocalFileName = 0;
2040     DWORD dwOffsetHeader = 0;
2041     DWORD dwFileSizeLow = 0;
2042     DWORD dwFileSizeHigh = 0;
2043     BYTE cDirectory = 0;
2044     char achFile[MAX_PATH];
2045     char achUrl[MAX_PATH];
2046     char *pchLocalFileName = 0;
2047
2048     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2049         debugstr_w(lpszUrlName),
2050         debugstr_w(lpszLocalFileName),
2051         CacheEntryType,
2052         lpHeaderInfo,
2053         dwHeaderSize,
2054         debugstr_w(lpszFileExtension),
2055         debugstr_w(lpszOriginalUrl));
2056
2057     if (lpszOriginalUrl)
2058         WARN(": lpszOriginalUrl ignored\n");
2059  
2060     if (lpszLocalFileName)
2061     {
2062         HANDLE hFile;
2063
2064         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2065         if (hFile == INVALID_HANDLE_VALUE)
2066         {
2067             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2068             return FALSE;
2069         }
2070
2071         /* Get file size */
2072         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2073         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2074         {
2075             ERR("couldn't get file size (error is %d)\n", GetLastError());
2076             CloseHandle(hFile);
2077             return FALSE;
2078         }
2079
2080         CloseHandle(hFile);
2081     }
2082
2083     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2084         return FALSE;
2085
2086     if (!URLCacheContainer_OpenIndex(pContainer))
2087         return FALSE;
2088
2089     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2090         return FALSE;
2091
2092     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
2093
2094     if (URLCache_FindEntryInHash(pHeader, achUrl, &pEntry))
2095     {
2096         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2097         FIXME("entry already in cache - don't know what to do!\n");
2098 /*
2099  *        SetLastError(ERROR_FILE_NOT_FOUND);
2100  *        return FALSE;
2101  */
2102         return TRUE;
2103     }
2104
2105     if (lpszLocalFileName)
2106     {
2107         BOOL bFound = FALSE;
2108
2109         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2110         {
2111             URLCacheContainer_UnlockIndex(pContainer, pHeader);
2112             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2113             SetLastError(ERROR_INVALID_PARAMETER);
2114             return FALSE;
2115         }
2116
2117         /* skip container path prefix */
2118         lpszLocalFileName += lstrlenW(pContainer->path);
2119
2120         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2121         pchLocalFileName = achFile;
2122
2123         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2124         {
2125             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2126             {
2127                 bFound = TRUE;
2128                 break;
2129             }
2130         }
2131
2132         if (!bFound)
2133         {
2134             URLCacheContainer_UnlockIndex(pContainer, pHeader);
2135             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2136             SetLastError(ERROR_INVALID_PARAMETER);
2137             return FALSE;
2138         }
2139
2140         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2141     }
2142
2143     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
2144     if (lpszLocalFileName)
2145     {
2146         dwOffsetLocalFileName = dwBytesNeeded;
2147         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2148     }
2149     if (lpHeaderInfo)
2150     {
2151         dwOffsetHeader = dwBytesNeeded;
2152         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2153     }
2154
2155     /* round up to next block */
2156     if (dwBytesNeeded % BLOCKSIZE)
2157     {
2158         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2159         dwBytesNeeded += BLOCKSIZE;
2160     }
2161
2162     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2163     {
2164         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2165         ERR("no free entries\n");
2166         SetLastError(ERROR_DISK_FULL);
2167         return FALSE;
2168     }
2169
2170     /* FindFirstFreeEntry fills in blocks used */
2171     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2172     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2173     pUrlEntry->CacheDir = cDirectory;
2174     pUrlEntry->CacheEntryType = CacheEntryType;
2175     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2176     pUrlEntry->dwExemptDelta = 0;
2177     pUrlEntry->dwHitRate = 0;
2178     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2179     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2180     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2181     pUrlEntry->dwSizeHigh = 0;
2182     pUrlEntry->dwSizeLow = dwFileSizeLow;
2183     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2184     pUrlEntry->dwUseCount = 0;
2185     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2186     pUrlEntry->LastModifiedTime = LastModifiedTime;
2187     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2188     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2189     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2190     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2191
2192     /*** Unknowns ***/
2193     pUrlEntry->dwUnknown1 = 0;
2194     pUrlEntry->dwUnknown2 = 0;
2195     pUrlEntry->dwUnknown3 = 0x60;
2196     pUrlEntry->Unknown4 = 0;
2197     pUrlEntry->wUnknown5 = 0x1010;
2198     pUrlEntry->dwUnknown6 = 0;
2199     pUrlEntry->dwUnknown7 = 0;
2200     pUrlEntry->dwUnknown8 = 0;
2201
2202
2203     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, achUrl);
2204     if (dwOffsetLocalFileName)
2205         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2206     if (dwOffsetHeader)
2207         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2208
2209     if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2210     {
2211         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2212         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2213         return FALSE;
2214     }
2215
2216     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2217
2218     return TRUE;
2219 }
2220
2221 /***********************************************************************
2222  *           CommitUrlCacheEntryA (WININET.@)
2223  *
2224  */
2225 BOOL WINAPI CommitUrlCacheEntryA(
2226     IN LPCSTR lpszUrlName,
2227     IN LPCSTR lpszLocalFileName,
2228     IN FILETIME ExpireTime,
2229     IN FILETIME LastModifiedTime,
2230     IN DWORD CacheEntryType,
2231     IN LPBYTE lpHeaderInfo,
2232     IN DWORD dwHeaderSize,
2233     IN LPCSTR lpszFileExtension,
2234     IN LPCSTR lpszOriginalUrl
2235     )
2236 {
2237     DWORD len;
2238     WCHAR *url_name;
2239     WCHAR *local_file_name;
2240     WCHAR *original_url = NULL;
2241     BOOL bSuccess = FALSE;
2242     DWORD dwError = 0;
2243
2244     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2245         debugstr_a(lpszUrlName),
2246         debugstr_a(lpszLocalFileName),
2247         CacheEntryType,
2248         lpHeaderInfo,
2249         dwHeaderSize,
2250         debugstr_a(lpszFileExtension),
2251         debugstr_a(lpszOriginalUrl));
2252
2253     if (lpszFileExtension != 0)
2254     {
2255         SetLastError(ERROR_INVALID_PARAMETER);
2256         return FALSE;
2257     }
2258     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2259         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2260     {
2261         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2262         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2263             (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2264         {
2265             MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2266             if (!lpszOriginalUrl ||
2267                 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2268                   (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2269             {
2270                 if (original_url)
2271                     MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2272                 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2273                                         CacheEntryType, lpHeaderInfo, dwHeaderSize,
2274                                         NULL, original_url))
2275                 {
2276                     bSuccess = TRUE;
2277                 }
2278                 else
2279                 {
2280                 dwError = GetLastError();
2281                 }
2282                 HeapFree(GetProcessHeap(), 0, original_url);
2283             }
2284                 else
2285             {
2286                 dwError = GetLastError();
2287             }
2288             HeapFree(GetProcessHeap(), 0, local_file_name);
2289         }
2290         else
2291         {
2292             dwError = GetLastError();
2293         }
2294         HeapFree(GetProcessHeap(), 0, url_name);
2295         if (!bSuccess)
2296             SetLastError(dwError);
2297     }
2298     return bSuccess;
2299 }
2300
2301 /***********************************************************************
2302  *           CommitUrlCacheEntryW (WININET.@)
2303  *
2304  */
2305 BOOL WINAPI CommitUrlCacheEntryW(
2306     IN LPCWSTR lpszUrlName,
2307     IN LPCWSTR lpszLocalFileName,
2308     IN FILETIME ExpireTime,
2309     IN FILETIME LastModifiedTime,
2310     IN DWORD CacheEntryType,
2311     IN LPWSTR lpHeaderInfo,
2312     IN DWORD dwHeaderSize,
2313     IN LPCWSTR lpszFileExtension,
2314     IN LPCWSTR lpszOriginalUrl
2315     )
2316 {
2317     DWORD dwError = 0;
2318     BOOL bSuccess = FALSE;
2319     DWORD len = 0;
2320     CHAR *header_info = NULL;
2321
2322     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2323         debugstr_w(lpszUrlName),
2324         debugstr_w(lpszLocalFileName),
2325         CacheEntryType,
2326         lpHeaderInfo,
2327         dwHeaderSize,
2328         debugstr_w(lpszFileExtension),
2329         debugstr_w(lpszOriginalUrl));
2330
2331     if (!lpHeaderInfo ||
2332         ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2333          (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2334     {
2335         if (header_info)
2336             WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2337         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2338                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2339         {
2340                 bSuccess = TRUE;
2341         }
2342         else
2343         {
2344                 dwError = GetLastError();
2345         }
2346         if (header_info)
2347         {
2348             HeapFree(GetProcessHeap(), 0, header_info);
2349             if (!bSuccess)
2350                 SetLastError(dwError);
2351         }
2352     }
2353     return bSuccess;
2354 }
2355
2356 /***********************************************************************
2357  *           ReadUrlCacheEntryStream (WININET.@)
2358  *
2359  */
2360 BOOL WINAPI ReadUrlCacheEntryStream(
2361     IN HANDLE hUrlCacheStream,
2362     IN  DWORD dwLocation,
2363     IN OUT LPVOID lpBuffer,
2364     IN OUT LPDWORD lpdwLen,
2365     IN DWORD dwReserved
2366     )
2367 {
2368     /* Get handle to file from 'stream' */
2369     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2370
2371     if (dwReserved != 0)
2372     {
2373         ERR("dwReserved != 0\n");
2374         SetLastError(ERROR_INVALID_PARAMETER);
2375         return FALSE;
2376     }
2377
2378     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2379     {
2380         SetLastError(ERROR_INVALID_HANDLE);
2381         return FALSE;
2382     }
2383
2384     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2385         return FALSE;
2386     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2387 }
2388
2389 /***********************************************************************
2390  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2391  *
2392  */
2393 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2394     IN LPCSTR lpszUrlName,
2395     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2396     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2397     IN BOOL fRandomRead,
2398     IN DWORD dwReserved
2399     )
2400 {
2401     /* NOTE: this is not the same as the way that the native
2402      * version allocates 'stream' handles. I did it this way
2403      * as it is much easier and no applications should depend
2404      * on this behaviour. (Native version appears to allocate
2405      * indices into a table)
2406      */
2407     STREAM_HANDLE * pStream;
2408     HANDLE hFile;
2409
2410     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2411            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2412
2413     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2414         lpCacheEntryInfo,
2415         lpdwCacheEntryInfoBufferSize,
2416         dwReserved))
2417     {
2418         return NULL;
2419     }
2420
2421     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2422         GENERIC_READ,
2423         FILE_SHARE_READ,
2424         NULL,
2425         OPEN_EXISTING,
2426         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2427         NULL);
2428     if (hFile == INVALID_HANDLE_VALUE)
2429         return FALSE;
2430     
2431     /* allocate handle storage space */
2432     pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2433     if (!pStream)
2434     {
2435         CloseHandle(hFile);
2436         SetLastError(ERROR_OUTOFMEMORY);
2437         return FALSE;
2438     }
2439
2440     pStream->hFile = hFile;
2441     strcpy(pStream->lpszUrl, lpszUrlName);
2442     return (HANDLE)pStream;
2443 }
2444
2445 /***********************************************************************
2446  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2447  *
2448  */
2449 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2450     IN LPCWSTR lpszUrlName,
2451     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2452     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2453     IN BOOL fRandomRead,
2454     IN DWORD dwReserved
2455     )
2456 {
2457     FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2458            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2459     return NULL;
2460 }
2461
2462 /***********************************************************************
2463  *           UnlockUrlCacheEntryStream (WININET.@)
2464  *
2465  */
2466 BOOL WINAPI UnlockUrlCacheEntryStream(
2467     IN HANDLE hUrlCacheStream,
2468     IN DWORD dwReserved
2469 )
2470 {
2471     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2472
2473     if (dwReserved != 0)
2474     {
2475         ERR("dwReserved != 0\n");
2476         SetLastError(ERROR_INVALID_PARAMETER);
2477         return FALSE;
2478     }
2479
2480     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2481     {
2482         SetLastError(ERROR_INVALID_HANDLE);
2483         return FALSE;
2484     }
2485
2486     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2487         return FALSE;
2488
2489     /* close file handle */
2490     CloseHandle(pStream->hFile);
2491
2492     /* free allocated space */
2493     HeapFree(GetProcessHeap(), 0, pStream);
2494
2495     return TRUE;
2496 }
2497
2498
2499 /***********************************************************************
2500  *           DeleteUrlCacheEntryA (WININET.@)
2501  *
2502  */
2503 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2504 {
2505     URLCACHECONTAINER * pContainer;
2506     LPURLCACHE_HEADER pHeader;
2507     CACHEFILE_ENTRY * pEntry;
2508
2509     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2510
2511     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2512         return FALSE;
2513
2514     if (!URLCacheContainer_OpenIndex(pContainer))
2515         return FALSE;
2516
2517     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2518         return FALSE;
2519
2520     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2521     {
2522         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2523         TRACE("entry %s not found!\n", lpszUrlName);
2524         SetLastError(ERROR_FILE_NOT_FOUND);
2525         return FALSE;
2526     }
2527
2528     URLCache_DeleteEntry(pHeader, pEntry);
2529
2530     URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2531
2532     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2533
2534     return TRUE;
2535 }
2536
2537 /***********************************************************************
2538  *           DeleteUrlCacheEntryW (WININET.@)
2539  *
2540  */
2541 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2542 {
2543     FIXME("(%s) stub\n", debugstr_w(lpszUrlName));
2544     return TRUE;
2545 }
2546
2547 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2548 {
2549     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2550     return TRUE;
2551 }
2552
2553 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2554 {
2555     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2556     return TRUE;
2557 }
2558
2559 /***********************************************************************
2560  *           CreateCacheContainerA (WININET.@)
2561  */
2562 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2563                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2564 {
2565     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2566           d1, d2, d3, d4, d5, d6, d7, d8);
2567     return TRUE;
2568 }
2569
2570 /***********************************************************************
2571  *           CreateCacheContainerW (WININET.@)
2572  */
2573 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2574                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2575 {
2576     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2577           d1, d2, d3, d4, d5, d6, d7, d8);
2578     return TRUE;
2579 }
2580
2581 /***********************************************************************
2582  *           FindCloseUrlCache (WININET.@)
2583  */
2584 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2585 {
2586     FIXME("(%p) stub\n", hEnumHandle);
2587     return TRUE;
2588 }
2589
2590 /***********************************************************************
2591  *           FindFirstUrlCacheContainerA (WININET.@)
2592  */
2593 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2594 {
2595     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2596     return NULL;
2597 }
2598
2599 /***********************************************************************
2600  *           FindFirstUrlCacheContainerW (WININET.@)
2601  */
2602 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2603 {
2604     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2605     return NULL;
2606 }
2607
2608 /***********************************************************************
2609  *           FindNextUrlCacheContainerA (WININET.@)
2610  */
2611 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2612 {
2613     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2614     return FALSE;
2615 }
2616
2617 /***********************************************************************
2618  *           FindNextUrlCacheContainerW (WININET.@)
2619  */
2620 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2621 {
2622     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2623     return FALSE;
2624 }
2625
2626 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2627   LPCSTR lpszUrlSearchPattern,
2628   DWORD dwFlags,
2629   DWORD dwFilter,
2630   GROUPID GroupId,
2631   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2632   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2633   LPVOID lpReserved,
2634   LPDWORD pcbReserved2,
2635   LPVOID lpReserved3
2636 )
2637 {
2638     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2639           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2640           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2641     SetLastError(ERROR_FILE_NOT_FOUND);
2642     return NULL;
2643 }
2644
2645 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2646   LPCWSTR lpszUrlSearchPattern,
2647   DWORD dwFlags,
2648   DWORD dwFilter,
2649   GROUPID GroupId,
2650   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2651   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2652   LPVOID lpReserved,
2653   LPDWORD pcbReserved2,
2654   LPVOID lpReserved3
2655 )
2656 {
2657     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2658           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2659           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2660     SetLastError(ERROR_FILE_NOT_FOUND);
2661     return NULL;
2662 }
2663
2664 /***********************************************************************
2665  *           FindFirstUrlCacheEntryA (WININET.@)
2666  *
2667  */
2668 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2669  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2670 {
2671   FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2672   SetLastError(ERROR_FILE_NOT_FOUND);
2673   return 0;
2674 }
2675
2676 /***********************************************************************
2677  *           FindFirstUrlCacheEntryW (WININET.@)
2678  *
2679  */
2680 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2681  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2682 {
2683   FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2684   return 0;
2685 }
2686
2687 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2688                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2689 {
2690     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2691           dwSearchCondition, lpGroupId, lpReserved);
2692     return NULL;
2693 }
2694
2695 BOOL WINAPI FindNextUrlCacheEntryA(
2696   HANDLE hEnumHandle,
2697   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2698   LPDWORD lpdwNextCacheEntryInfoBufferSize
2699 )
2700 {
2701     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2702     return FALSE;
2703 }
2704
2705 BOOL WINAPI FindNextUrlCacheEntryW(
2706   HANDLE hEnumHandle,
2707   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2708   LPDWORD lpdwNextCacheEntryInfoBufferSize
2709 )
2710 {
2711     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2712     return FALSE;
2713 }
2714
2715 BOOL WINAPI FindNextUrlCacheEntryExA(
2716   HANDLE hEnumHandle,
2717   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2718   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2719   LPVOID lpReserved,
2720   LPDWORD pcbReserved2,
2721   LPVOID lpReserved3
2722 )
2723 {
2724     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2725           lpReserved, pcbReserved2, lpReserved3);
2726     return FALSE;
2727 }
2728
2729 BOOL WINAPI FindNextUrlCacheEntryExW(
2730   HANDLE hEnumHandle,
2731   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2732   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2733   LPVOID lpReserved,
2734   LPDWORD pcbReserved2,
2735   LPVOID lpReserved3
2736 )
2737 {
2738     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2739           lpReserved, pcbReserved2, lpReserved3);
2740     return FALSE;
2741 }
2742
2743 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2744 {
2745     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2746     return FALSE;
2747 }
2748
2749 /***********************************************************************
2750  *           CreateUrlCacheGroup (WININET.@)
2751  *
2752  */
2753 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2754 {
2755   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
2756   return FALSE;
2757 }
2758
2759 /***********************************************************************
2760  *           DeleteUrlCacheGroup (WININET.@)
2761  *
2762  */
2763 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2764 {
2765     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
2766           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2767     return FALSE;
2768 }
2769
2770 /***********************************************************************
2771  *           SetUrlCacheEntryGroupA (WININET.@)
2772  *
2773  */
2774 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2775   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2776   LPVOID lpReserved)
2777 {
2778     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2779           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2780           pbGroupAttributes, cbGroupAttributes, lpReserved);
2781     SetLastError(ERROR_FILE_NOT_FOUND);
2782     return FALSE;
2783 }
2784
2785 /***********************************************************************
2786  *           SetUrlCacheEntryGroupW (WININET.@)
2787  *
2788  */
2789 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2790   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2791   LPVOID lpReserved)
2792 {
2793     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2794           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2795           pbGroupAttributes, cbGroupAttributes, lpReserved);
2796     SetLastError(ERROR_FILE_NOT_FOUND);
2797     return FALSE;
2798 }
2799
2800 /***********************************************************************
2801  *           GetUrlCacheConfigInfoW (WININET.@)
2802  */
2803 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
2804 {
2805     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2806     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2807     return FALSE;
2808 }
2809
2810 /***********************************************************************
2811  *           GetUrlCacheConfigInfoA (WININET.@)
2812  *
2813  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2814  */
2815 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
2816 {
2817     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2818     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2819     return FALSE;
2820 }
2821
2822 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2823                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2824                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2825 {
2826     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2827           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2828           lpdwGroupInfo, lpReserved);
2829     return FALSE;
2830 }
2831
2832 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2833                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2834                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2835 {
2836     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2837           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2838           lpdwGroupInfo, lpReserved);
2839     return FALSE;
2840 }
2841
2842 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2843                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2844 {
2845     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2846           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2847     return TRUE;
2848 }
2849
2850 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2851                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2852 {
2853     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2854           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2855     return TRUE;
2856 }
2857
2858 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
2859 {
2860     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2861     return TRUE;
2862 }
2863
2864 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
2865 {
2866     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2867     return TRUE;
2868 }
2869
2870 /***********************************************************************
2871  *           DeleteIE3Cache (WININET.@)
2872  *
2873  * Deletes the files used by the IE3 URL caching system.
2874  *
2875  * PARAMS
2876  *   hWnd        [I] A dummy window.
2877  *   hInst       [I] Instance of process calling the function.
2878  *   lpszCmdLine [I] Options used by function.
2879  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
2880  */
2881 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2882 {
2883     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
2884     return 0;
2885 }
2886
2887 /***********************************************************************
2888  *           IsUrlCacheEntryExpiredA (WININET.@)
2889  *
2890  * PARAMS
2891  *   url        [I]    Url
2892  *   dwFlags           Unknown
2893  *   pftLastModified   Unknown ptr
2894  */
2895 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
2896 {
2897     FIXME("(%s, %08x, %p) stub\n", debugstr_a(url), dwFlags, pftLastModified);
2898     return FALSE;
2899 }
2900
2901 /***********************************************************************
2902  *           IsUrlCacheEntryExpiredW (WININET.@)
2903  *
2904  * PARAMS
2905  *   url        [I]    Url
2906  *   dwFlags           Unknown
2907  *   pftLastModified   Unknown ptr
2908  */
2909 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
2910 {
2911     FIXME("(%s, %08x, %p) stub\n", debugstr_w(url), dwFlags, pftLastModified);
2912     return FALSE;
2913 }