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