wine.inf: We should not override existing associations.
[wine/hacks.git] / dlls / wininet / urlcache.c
blob15fc4eb33a4dd9f31af5f8cdc657a00a28fd88d4
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
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.
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.
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
25 #define COM_NO_WINDOWS_H
26 #include "config.h"
27 #include "wine/port.h"
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
38 #include "windef.h"
39 #include "winbase.h"
40 #include "winuser.h"
41 #include "wininet.h"
42 #include "winerror.h"
43 #include "internet.h"
44 #include "winreg.h"
45 #include "shlwapi.h"
46 #include "wingdi.h"
47 #include "shlobj.h"
49 #include "wine/unicode.h"
50 #include "wine/list.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
55 #define ENTRY_START_OFFSET 0x4000
56 #define DIR_LENGTH 8
57 #define BLOCKSIZE 128
58 #define HASHTABLE_SIZE 448
59 #define HASHTABLE_BLOCKSIZE 7
60 #define HASHTABLE_FREE 3
61 #define ALLOCATION_TABLE_OFFSET 0x250
62 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
63 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
64 #define NEWFILE_NUM_BLOCKS 0xd80
65 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
67 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
68 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
69 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
70 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
71 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
73 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
75 typedef struct _CACHEFILE_ENTRY
77 /* union
78 {*/
79 DWORD dwSignature; /* e.g. "URL " */
80 /* CHAR szSignature[4];
81 };*/
82 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
83 } CACHEFILE_ENTRY;
85 typedef struct _URL_CACHEFILE_ENTRY
87 CACHEFILE_ENTRY CacheFileEntry;
88 FILETIME LastModifiedTime;
89 FILETIME LastAccessTime;
90 WORD wExpiredDate; /* expire date in dos format */
91 WORD wExpiredTime; /* expire time in dos format */
92 DWORD dwUnknown1; /* usually zero */
93 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
94 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
95 DWORD dwUnknown2; /* usually zero */
96 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
97 DWORD dwUnknown3; /* usually 0x60 */
98 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
99 BYTE CacheDir; /* index of cache directory this url is stored in */
100 BYTE Unknown4; /* usually zero */
101 WORD wUnknown5; /* usually 0x1010 */
102 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
103 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
104 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
105 DWORD dwHeaderInfoSize;
106 DWORD dwUnknown6; /* usually zero */
107 WORD wLastSyncDate; /* last sync date in dos format */
108 WORD wLastSyncTime; /* last sync time in dos format */
109 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
110 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
111 WORD wUnknownDate; /* usually same as wLastSyncDate */
112 WORD wUnknownTime; /* usually same as wLastSyncTime */
113 DWORD dwUnknown7; /* usually zero */
114 DWORD dwUnknown8; /* usually zero */
115 /* packing to dword align start of next field */
116 /* CHAR szSourceUrlName[]; (url) */
117 /* packing to dword align start of next field */
118 /* CHAR szLocalFileName[]; (local file name exluding path) */
119 /* packing to dword align start of next field */
120 /* CHAR szHeaderInfo[]; (header info) */
121 } URL_CACHEFILE_ENTRY;
123 struct _HASH_ENTRY
125 DWORD dwHashKey;
126 DWORD dwOffsetEntry;
129 typedef struct _HASH_CACHEFILE_ENTRY
131 CACHEFILE_ENTRY CacheFileEntry;
132 DWORD dwAddressNext;
133 DWORD dwHashTableNumber;
134 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
135 } HASH_CACHEFILE_ENTRY;
137 typedef struct _DIRECTORY_DATA
139 DWORD dwUnknown;
140 char filename[DIR_LENGTH];
141 } DIRECTORY_DATA;
143 typedef struct _URLCACHE_HEADER
145 char szSignature[28];
146 DWORD dwFileSize;
147 DWORD dwOffsetFirstHashTable;
148 DWORD dwIndexCapacityInBlocks;
149 DWORD dwBlocksInUse;
150 DWORD dwUnknown1;
151 DWORD dwCacheLimitLow; /* disk space limit for cache */
152 DWORD dwCacheLimitHigh; /* disk space limit for cache */
153 DWORD dwUnknown4; /* current disk space usage for cache */
154 DWORD dwUnknown5; /* current disk space usage for cache */
155 DWORD dwUnknown6; /* possibly a flag? */
156 DWORD dwUnknown7;
157 BYTE DirectoryCount; /* number of directory_data's */
158 BYTE Unknown8[3]; /* just padding? */
159 DIRECTORY_DATA directory_data[1]; /* first directory entry */
160 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
161 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
163 typedef struct _STREAM_HANDLE
165 HANDLE hFile;
166 CHAR lpszUrl[1];
167 } STREAM_HANDLE;
169 typedef struct _URLCACHECONTAINER
171 struct list entry; /* part of a list */
172 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
173 LPWSTR path; /* path to url container directory */
174 HANDLE hMapping; /* handle of file mapping */
175 DWORD file_size; /* size of file when mapping was opened */
176 HANDLE hMutex; /* hande of mutex */
177 } URLCACHECONTAINER;
180 /* List of all containers available */
181 static struct list UrlContainers = LIST_INIT(UrlContainers);
183 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash);
185 /***********************************************************************
186 * URLCache_PathToObjectName (Internal)
188 * Converts a path to a name suitable for use as a Win32 object name.
189 * Replaces '\\' characters in-place with the specified character
190 * (usually '_' or '!')
192 * RETURNS
193 * nothing
196 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
198 for (; *lpszPath; lpszPath++)
200 if (*lpszPath == '\\')
201 *lpszPath = replace;
205 /***********************************************************************
206 * URLCacheContainer_OpenIndex (Internal)
208 * Opens the index file and saves mapping handle in hCacheIndexMapping
210 * RETURNS
211 * TRUE if succeeded
212 * FALSE if failed
215 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
217 HANDLE hFile;
218 WCHAR wszFilePath[MAX_PATH];
219 DWORD dwFileSize;
221 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
222 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
224 if (pContainer->hMapping)
225 return TRUE;
227 strcpyW(wszFilePath, pContainer->path);
228 strcatW(wszFilePath, wszIndex);
230 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
231 if (hFile == INVALID_HANDLE_VALUE)
233 /* Maybe the directory wasn't there? Try to create it */
234 if (CreateDirectoryW(pContainer->path, 0))
235 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
237 if (hFile == INVALID_HANDLE_VALUE)
239 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
240 return FALSE;
243 /* At this stage we need the mutex because we may be about to create the
244 * file.
246 WaitForSingleObject(pContainer->hMutex, INFINITE);
248 dwFileSize = GetFileSize(hFile, NULL);
249 if (dwFileSize == INVALID_FILE_SIZE)
251 ReleaseMutex(pContainer->hMutex);
252 return FALSE;
255 if (dwFileSize == 0)
257 static CHAR const szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Cache\\Content";
258 HKEY key;
259 char achZeroes[0x1000];
260 DWORD dwOffset;
261 DWORD dwError = 0;
263 /* Write zeroes to the entire file so we can safely map it without
264 * fear of getting a SEGV because the disk is full.
266 memset(achZeroes, 0, sizeof(achZeroes));
267 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
269 DWORD dwWrite = sizeof(achZeroes);
270 DWORD dwWritten;
272 if (NEWFILE_SIZE - dwOffset < dwWrite)
273 dwWrite = NEWFILE_SIZE - dwOffset;
274 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
275 dwWritten != dwWrite)
277 /* If we fail to write, we need to return the error that
278 * cause the problem and also make sure the file is no
279 * longer there, if possible.
281 dwError = GetLastError();
283 break;
287 if (!dwError)
289 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
291 if (hMapping)
293 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
295 if (pHeader)
297 WCHAR *pwchDir;
298 WCHAR wszDirPath[MAX_PATH];
299 FILETIME ft;
300 int i, j;
302 dwFileSize = NEWFILE_SIZE;
304 /* First set some constants and defaults in the header */
305 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
306 pHeader->dwFileSize = dwFileSize;
307 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
308 /* 127MB - taken from default for Windows 2000 */
309 pHeader->dwCacheLimitHigh = 0;
310 pHeader->dwCacheLimitLow = 0x07ff5400;
311 /* Copied from a Windows 2000 cache index */
312 pHeader->DirectoryCount = 4;
314 /* If the registry has a cache size set, use the registry value */
315 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
317 DWORD dw;
318 DWORD len = sizeof(dw);
319 DWORD keytype;
321 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
322 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
323 keytype == REG_DWORD)
325 pHeader->dwCacheLimitHigh = (dw >> 22);
326 pHeader->dwCacheLimitLow = dw << 10;
328 RegCloseKey(key);
331 URLCache_CreateHashTable(pHeader, NULL);
333 /* Last step - create the directories */
335 strcpyW(wszDirPath, pContainer->path);
336 pwchDir = wszDirPath + strlenW(wszDirPath);
337 pwchDir[8] = 0;
339 GetSystemTimeAsFileTime(&ft);
341 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
343 /* The following values were copied from a Windows index.
344 * I don't know what the values are supposed to mean but
345 * have made them the same in the hope that this will
346 * be better for compatibility
348 pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
349 for (j = 0;; ++j)
351 int k;
352 ULONGLONG n = ft.dwHighDateTime;
354 /* Generate a file name to attempt to create.
355 * This algorithm will create what will appear
356 * to be random and unrelated directory names
357 * of up to 9 characters in length.
359 n <<= 32;
360 n += ft.dwLowDateTime;
361 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
363 for (k = 0; k < 8; ++k)
365 int r = (n % 36);
367 /* Dividing by a prime greater than 36 helps
368 * with the appearance of randomness
370 n /= 37;
372 if (r < 10)
373 pwchDir[k] = '0' + r;
374 else
375 pwchDir[k] = 'A' + (r - 10);
378 if (CreateDirectoryW(wszDirPath, 0))
380 int k;
382 /* The following is OK because we generated an
383 * 8 character directory name made from characters
384 * [A-Z0-9], which are equivalent for all code
385 * pages and for UTF-16
387 for (k = 0; k < 8; ++k)
388 pHeader->directory_data[i].filename[k] = pwchDir[k];
389 break;
391 else if (j >= 255)
393 /* Give up. The most likely cause of this
394 * is a full disk, but whatever the cause
395 * is, it should be more than apparent that
396 * we won't succeed.
398 dwError = GetLastError();
399 break;
404 UnmapViewOfFile(pHeader);
406 else
408 dwError = GetLastError();
410 CloseHandle(hMapping);
412 else
414 dwError = GetLastError();
418 if (dwError)
420 CloseHandle(hFile);
421 DeleteFileW(wszFilePath);
422 ReleaseMutex(pContainer->hMutex);
423 SetLastError(dwError);
424 return FALSE;
429 ReleaseMutex(pContainer->hMutex);
431 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
432 URLCache_PathToObjectName(wszFilePath, '_');
433 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
434 if (!pContainer->hMapping)
435 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
436 CloseHandle(hFile);
437 if (!pContainer->hMapping)
439 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
440 return FALSE;
443 return TRUE;
446 /***********************************************************************
447 * URLCacheContainer_CloseIndex (Internal)
449 * Closes the index
451 * RETURNS
452 * nothing
455 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
457 CloseHandle(pContainer->hMapping);
458 pContainer->hMapping = NULL;
461 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
463 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
464 int path_len = strlenW(path);
465 int cache_prefix_len = strlenW(cache_prefix);
467 if (!pContainer)
469 return FALSE;
472 pContainer->hMapping = NULL;
473 pContainer->file_size = 0;
475 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
476 if (!pContainer->path)
478 HeapFree(GetProcessHeap(), 0, pContainer);
479 return FALSE;
482 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
484 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
485 if (!pContainer->cache_prefix)
487 HeapFree(GetProcessHeap(), 0, pContainer->path);
488 HeapFree(GetProcessHeap(), 0, pContainer);
489 return FALSE;
492 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
494 CharLowerW(mutex_name);
495 URLCache_PathToObjectName(mutex_name, '!');
497 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
499 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
500 HeapFree(GetProcessHeap(), 0, pContainer->path);
501 HeapFree(GetProcessHeap(), 0, pContainer);
502 return FALSE;
505 list_add_head(&UrlContainers, &pContainer->entry);
507 return TRUE;
510 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
512 list_remove(&pContainer->entry);
514 URLCacheContainer_CloseIndex(pContainer);
515 CloseHandle(pContainer->hMutex);
516 HeapFree(GetProcessHeap(), 0, pContainer->path);
517 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
518 HeapFree(GetProcessHeap(), 0, pContainer);
521 void URLCacheContainers_CreateDefaults(void)
523 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
524 static const WCHAR UrlPrefix[] = {0};
525 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
526 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
527 static const WCHAR CookieSuffix[] = {0};
528 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
529 static const struct
531 int nFolder; /* CSIDL_* constant */
532 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
533 const WCHAR * cache_prefix; /* prefix used to reference the container */
534 } DefaultContainerData[] =
536 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
537 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
538 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
540 DWORD i;
542 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
544 WCHAR wszCachePath[MAX_PATH];
545 WCHAR wszMutexName[MAX_PATH];
546 int path_len, suffix_len;
548 if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
550 ERR("Couldn't get path for default container %lu\n", i);
551 continue;
553 path_len = strlenW(wszCachePath);
554 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
556 if (path_len + suffix_len + 2 > MAX_PATH)
558 ERR("Path too long\n");
559 continue;
562 wszCachePath[path_len] = '\\';
563 wszCachePath[path_len+1] = 0;
565 strcpyW(wszMutexName, wszCachePath);
567 if (suffix_len)
569 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
570 wszCachePath[path_len + suffix_len + 1] = '\\';
571 wszCachePath[path_len + suffix_len + 2] = '\0';
574 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
578 void URLCacheContainers_DeleteAll(void)
580 while(!list_empty(&UrlContainers))
581 URLCacheContainer_DeleteContainer(
582 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
586 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
588 struct list * cursor;
590 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
592 LIST_FOR_EACH(cursor, &UrlContainers)
594 URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
595 int prefix_len = strlenW(pContainer->cache_prefix);
596 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
598 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
599 *ppContainer = pContainer;
600 return TRUE;
603 ERR("no container found\n");
604 SetLastError(ERROR_FILE_NOT_FOUND);
605 return FALSE;
608 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
610 BOOL ret;
611 LPWSTR lpwszUrl;
612 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
613 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
615 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
616 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
617 HeapFree(GetProcessHeap(), 0, lpwszUrl);
618 return ret;
620 return FALSE;
623 /***********************************************************************
624 * URLCacheContainer_LockIndex (Internal)
627 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
629 BYTE index;
630 LPVOID pIndexData;
631 URLCACHE_HEADER * pHeader;
633 /* acquire mutex */
634 WaitForSingleObject(pContainer->hMutex, INFINITE);
636 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
638 if (!pIndexData)
640 ReleaseMutex(pContainer->hMutex);
641 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
642 return FALSE;
644 pHeader = (URLCACHE_HEADER *)pIndexData;
646 /* file has grown - we need to remap to prevent us getting
647 * access violations when we try and access beyond the end
648 * of the memory mapped file */
649 if (pHeader->dwFileSize != pContainer->file_size)
651 URLCacheContainer_CloseIndex(pContainer);
652 if (!URLCacheContainer_OpenIndex(pContainer))
654 ReleaseMutex(pContainer->hMutex);
655 return FALSE;
657 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
659 if (!pIndexData)
661 ReleaseMutex(pContainer->hMutex);
662 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
663 return FALSE;
665 pHeader = (URLCACHE_HEADER *)pIndexData;
668 TRACE("Signature: %s, file size: %ld bytes\n", pHeader->szSignature, pHeader->dwFileSize);
670 for (index = 0; index < pHeader->DirectoryCount; index++)
672 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
675 return pHeader;
678 /***********************************************************************
679 * URLCacheContainer_UnlockIndex (Internal)
682 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
684 /* release mutex */
685 ReleaseMutex(pContainer->hMutex);
686 return UnmapViewOfFile(pHeader);
690 #ifndef CHAR_BIT
691 #define CHAR_BIT (8 * sizeof(CHAR))
692 #endif
694 /***********************************************************************
695 * URLCache_Allocation_BlockIsFree (Internal)
697 * Is the specified block number free?
699 * RETURNS
700 * zero if free
701 * non-zero otherwise
704 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
706 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
707 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
710 /***********************************************************************
711 * URLCache_Allocation_BlockFree (Internal)
713 * Marks the specified block as free
715 * RETURNS
716 * nothing
719 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
721 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
722 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
725 /***********************************************************************
726 * URLCache_Allocation_BlockAllocate (Internal)
728 * Marks the specified block as allocated
730 * RETURNS
731 * nothing
734 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
736 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
737 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
740 /***********************************************************************
741 * URLCache_FindFirstFreeEntry (Internal)
743 * Finds and allocates the first block of free space big enough and
744 * sets ppEntry to point to it.
746 * RETURNS
747 * TRUE if it had enough space
748 * FALSE if it couldn't find enough space
751 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
753 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
754 DWORD dwBlockNumber;
755 DWORD dwFreeCounter;
756 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
758 for (dwFreeCounter = 0;
759 dwFreeCounter < dwBlocksNeeded &&
760 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
761 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
762 dwFreeCounter++)
763 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
765 if (dwFreeCounter == dwBlocksNeeded)
767 DWORD index;
768 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
769 for (index = 0; index < dwBlocksNeeded; index++)
770 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
771 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
772 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
773 return TRUE;
776 FIXME("Grow file\n");
777 return FALSE;
780 /***********************************************************************
781 * URLCache_DeleteEntry (Internal)
783 * Deletes the specified entry and frees the space allocated to it
785 * RETURNS
786 * TRUE if it succeeded
787 * FALSE if it failed
790 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
792 DWORD dwStartBlock;
793 DWORD dwBlock;
794 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
796 /* update allocation table */
797 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
798 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
799 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
801 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
802 return TRUE;
805 /***********************************************************************
806 * URLCache_LocalFileNameToPathW (Internal)
808 * Copies the full path to the specified buffer given the local file
809 * name and the index of the directory it is in. Always sets value in
810 * lpBufferSize to the required buffer size (in bytes).
812 * RETURNS
813 * TRUE if the buffer was big enough
814 * FALSE if the buffer was too small
817 static BOOL URLCache_LocalFileNameToPathW(
818 const URLCACHECONTAINER * pContainer,
819 LPCURLCACHE_HEADER pHeader,
820 LPCSTR szLocalFileName,
821 BYTE Directory,
822 LPWSTR wszPath,
823 LPLONG lpBufferSize)
825 LONG nRequired;
826 int path_len = strlenW(pContainer->path);
827 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
828 if (Directory >= pHeader->DirectoryCount)
830 *lpBufferSize = 0;
831 return FALSE;
834 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
835 if (nRequired < *lpBufferSize)
837 int dir_len;
839 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
840 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
841 wszPath[dir_len + path_len] = '\\';
842 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
843 *lpBufferSize = nRequired;
844 return TRUE;
846 *lpBufferSize = nRequired;
847 return FALSE;
850 /***********************************************************************
851 * URLCache_LocalFileNameToPathA (Internal)
853 * Copies the full path to the specified buffer given the local file
854 * name and the index of the directory it is in. Always sets value in
855 * lpBufferSize to the required buffer size.
857 * RETURNS
858 * TRUE if the buffer was big enough
859 * FALSE if the buffer was too small
862 static BOOL URLCache_LocalFileNameToPathA(
863 const URLCACHECONTAINER * pContainer,
864 LPCURLCACHE_HEADER pHeader,
865 LPCSTR szLocalFileName,
866 BYTE Directory,
867 LPSTR szPath,
868 LPLONG lpBufferSize)
870 LONG nRequired;
871 int path_len, file_name_len, dir_len;
873 if (Directory >= pHeader->DirectoryCount)
875 *lpBufferSize = 0;
876 return FALSE;
879 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
880 file_name_len = strlen(szLocalFileName);
881 dir_len = DIR_LENGTH;
883 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(WCHAR);
884 if (nRequired < *lpBufferSize)
886 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
887 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
888 szPath[path_len + dir_len] = '\\';
889 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
890 *lpBufferSize = nRequired;
891 return TRUE;
893 *lpBufferSize = nRequired;
894 return FALSE;
897 /***********************************************************************
898 * URLCache_CopyEntry (Internal)
900 * Copies an entry from the cache index file to the Win32 structure
902 * RETURNS
903 * TRUE if the buffer was big enough
904 * FALSE if the buffer was too small
907 static BOOL URLCache_CopyEntry(
908 URLCACHECONTAINER * pContainer,
909 LPCURLCACHE_HEADER pHeader,
910 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
911 LPDWORD lpdwBufferSize,
912 URL_CACHEFILE_ENTRY * pUrlEntry,
913 BOOL bUnicode)
915 int lenUrl;
916 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
918 if (*lpdwBufferSize >= dwRequiredSize)
920 lpCacheEntryInfo->lpHeaderInfo = NULL;
921 lpCacheEntryInfo->lpszFileExtension = NULL;
922 lpCacheEntryInfo->lpszLocalFileName = NULL;
923 lpCacheEntryInfo->lpszSourceUrlName = NULL;
924 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
925 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
926 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
927 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
928 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
929 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
930 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
931 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
932 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
933 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
934 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
935 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
936 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
937 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
940 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
941 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
942 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
943 if (bUnicode)
944 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
945 else
946 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
947 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
949 /* FIXME: is source url optional? */
950 if (*lpdwBufferSize >= dwRequiredSize)
952 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
953 if (bUnicode)
954 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
955 else
956 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
959 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
960 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
961 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
963 if (pUrlEntry->dwOffsetLocalName)
965 LONG nLocalFilePathSize;
966 LPSTR lpszLocalFileName;
967 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
968 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
969 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
970 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
972 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
974 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
976 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
977 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
978 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
980 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
982 if (*lpdwBufferSize >= dwRequiredSize)
984 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
985 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
986 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
988 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
989 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
990 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
992 if (dwRequiredSize > *lpdwBufferSize)
994 *lpdwBufferSize = dwRequiredSize;
995 SetLastError(ERROR_INSUFFICIENT_BUFFER);
996 return FALSE;
998 *lpdwBufferSize = dwRequiredSize;
999 return TRUE;
1003 /***********************************************************************
1004 * URLCache_SetEntryInfo (Internal)
1006 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1007 * according the the flags set by dwFieldControl.
1009 * RETURNS
1010 * TRUE if the buffer was big enough
1011 * FALSE if the buffer was too small
1014 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1016 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1017 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1018 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1019 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1020 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1021 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1022 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1023 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1024 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1025 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1026 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1027 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1028 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1029 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1030 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1031 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1033 return TRUE;
1036 /***********************************************************************
1037 * URLCache_HashKey (Internal)
1039 * Returns the hash key for a given string
1041 * RETURNS
1042 * hash key for the string
1045 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1047 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1048 * but the algorithm and result are not the same!
1050 static const unsigned char lookupTable[256] =
1052 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1053 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1054 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1055 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1056 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1057 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1058 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1059 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1060 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1061 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1062 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1063 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1064 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1065 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1066 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1067 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1068 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1069 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1070 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1071 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1072 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1073 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1074 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1075 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1076 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1077 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1078 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1079 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1080 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1081 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1082 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1083 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1085 BYTE key[4];
1086 DWORD i;
1087 int subscript[sizeof(key) / sizeof(key[0])];
1089 subscript[0] = *lpszKey;
1090 subscript[1] = (char)(*lpszKey + 1);
1091 subscript[2] = (char)(*lpszKey + 2);
1092 subscript[3] = (char)(*lpszKey + 3);
1094 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1095 key[i] = lookupTable[i];
1097 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1099 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1100 key[i] = lookupTable[*lpszKey ^ key[i]];
1103 return *(DWORD *)key;
1106 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1108 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1111 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1113 /* structure of hash table:
1114 * 448 entries divided into 64 blocks
1115 * each block therefore contains a chain of 7 key/offset pairs
1116 * how position in table is calculated:
1117 * 1. the url is hashed in helper function
1118 * 2. the key % 64 * 8 is the offset
1119 * 3. the key in the hash table is the hash key aligned to 64
1121 * note:
1122 * there can be multiple hash tables in the file and the offset to
1123 * the next one is stored in the header of the hash table
1125 DWORD key = URLCache_HashKey(lpszUrl);
1126 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1127 HASH_CACHEFILE_ENTRY * pHashEntry;
1128 DWORD dwHashTableNumber = 0;
1130 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1132 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1133 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1134 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1136 int i;
1137 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1139 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1140 continue;
1142 /* make sure that it is in fact a hash entry */
1143 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1145 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1146 continue;
1149 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1151 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1152 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1154 /* FIXME: we should make sure that this is the right element
1155 * before returning and claiming that it is. We can do this
1156 * by doing a simple compare between the URL we were given
1157 * and the URL stored in the entry. However, this assumes
1158 * we know the format of all the entries stored in the
1159 * hash table */
1160 *ppHashEntry = pHashElement;
1161 return TRUE;
1165 return FALSE;
1168 /***********************************************************************
1169 * URLCache_FindEntryInHash (Internal)
1171 * Searches all the hash tables in the index for the given URL and
1172 * returns the entry, if it was found, in ppEntry
1174 * RETURNS
1175 * TRUE if the entry was found
1176 * FALSE if the entry could not be found
1179 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
1181 struct _HASH_ENTRY * pHashEntry;
1182 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1184 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1185 return TRUE;
1187 return FALSE;
1190 /***********************************************************************
1191 * URLCache_HashEntrySetUse (Internal)
1193 * Searches all the hash tables in the index for the given URL and
1194 * sets the use count (stored or'ed with key)
1196 * RETURNS
1197 * TRUE if the entry was found
1198 * FALSE if the entry could not be found
1201 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1203 struct _HASH_ENTRY * pHashEntry;
1204 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1206 pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1207 return TRUE;
1209 return FALSE;
1212 /***********************************************************************
1213 * URLCache_DeleteEntryFromHash (Internal)
1215 * Searches all the hash tables in the index for the given URL and
1216 * then if found deletes the entry.
1218 * RETURNS
1219 * TRUE if the entry was found
1220 * FALSE if the entry could not be found
1223 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1225 struct _HASH_ENTRY * pHashEntry;
1226 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1228 pHashEntry->dwHashKey = HASHTABLE_FREE;
1229 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1230 return TRUE;
1232 return FALSE;
1235 /***********************************************************************
1236 * URLCache_AddEntryToHash (Internal)
1238 * Searches all the hash tables for a free slot based on the offset
1239 * generated from the hash key. If a free slot is found, the offset and
1240 * key are entered into the hash table.
1242 * RETURNS
1243 * TRUE if the entry was added
1244 * FALSE if the entry could not be added
1247 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1249 /* see URLCache_FindEntryInHash for structure of hash tables */
1251 DWORD key = URLCache_HashKey(lpszUrl);
1252 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1253 HASH_CACHEFILE_ENTRY * pHashEntry;
1254 DWORD dwHashTableNumber = 0;
1256 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1258 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1259 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1260 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1262 int i;
1263 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1265 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1266 break;
1268 /* make sure that it is in fact a hash entry */
1269 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1271 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1272 break;
1275 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1277 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1278 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1280 pHashElement->dwHashKey = key;
1281 pHashElement->dwOffsetEntry = dwOffsetEntry;
1282 return TRUE;
1286 pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1287 if (!pHashEntry)
1288 return FALSE;
1290 pHashEntry->HashTable[offset].dwHashKey = key;
1291 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1292 return TRUE;
1295 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1297 HASH_CACHEFILE_ENTRY *pHash;
1298 DWORD dwOffset;
1299 int i;
1301 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1303 FIXME("no free space for hash table\n");
1304 SetLastError(ERROR_DISK_FULL);
1305 return NULL;
1308 dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1310 if (pPrevHash)
1311 pPrevHash->dwAddressNext = dwOffset;
1312 else
1313 pHeader->dwOffsetFirstHashTable = dwOffset;
1314 pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1315 pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1316 pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1317 for (i = 0; i < HASHTABLE_SIZE; i++)
1319 pHash->HashTable[i].dwOffsetEntry = 0;
1320 pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1322 return pHash;
1325 /***********************************************************************
1326 * GetUrlCacheEntryInfoExA (WININET.@)
1329 BOOL WINAPI GetUrlCacheEntryInfoExA(
1330 LPCSTR lpszUrl,
1331 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1332 LPDWORD lpdwCacheEntryInfoBufSize,
1333 LPSTR lpszReserved,
1334 LPDWORD lpdwReserved,
1335 LPVOID lpReserved,
1336 DWORD dwFlags)
1338 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1339 debugstr_a(lpszUrl),
1340 lpCacheEntryInfo,
1341 lpdwCacheEntryInfoBufSize,
1342 lpszReserved,
1343 lpdwReserved,
1344 lpReserved,
1345 dwFlags);
1347 if ((lpszReserved != NULL) ||
1348 (lpdwReserved != NULL) ||
1349 (lpReserved != NULL))
1351 ERR("Reserved value was not 0\n");
1352 SetLastError(ERROR_INVALID_PARAMETER);
1353 return FALSE;
1355 if (dwFlags != 0)
1356 FIXME("Undocumented flag(s): %lx\n", dwFlags);
1357 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1360 /***********************************************************************
1361 * GetUrlCacheEntryInfoA (WININET.@)
1364 BOOL WINAPI GetUrlCacheEntryInfoA(
1365 IN LPCSTR lpszUrlName,
1366 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1367 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1370 LPURLCACHE_HEADER pHeader;
1371 CACHEFILE_ENTRY * pEntry;
1372 URL_CACHEFILE_ENTRY * pUrlEntry;
1373 URLCACHECONTAINER * pContainer;
1375 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1377 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1378 return FALSE;
1380 if (!URLCacheContainer_OpenIndex(pContainer))
1381 return FALSE;
1383 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1384 return FALSE;
1386 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1388 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1389 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1390 SetLastError(ERROR_FILE_NOT_FOUND);
1391 return FALSE;
1394 if (pEntry->dwSignature != URL_SIGNATURE)
1396 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1397 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1398 SetLastError(ERROR_FILE_NOT_FOUND);
1399 return FALSE;
1402 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1403 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1404 if (pUrlEntry->dwOffsetHeaderInfo)
1405 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1407 if (!URLCache_CopyEntry(
1408 pContainer,
1409 pHeader,
1410 lpCacheEntryInfo,
1411 lpdwCacheEntryInfoBufferSize,
1412 pUrlEntry,
1413 FALSE /* ANSI */))
1415 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1416 return FALSE;
1418 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1420 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1422 return TRUE;
1425 /***********************************************************************
1426 * GetUrlCacheEntryInfoW (WININET.@)
1429 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1430 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1431 LPDWORD lpdwCacheEntryInfoBufferSize)
1433 LPURLCACHE_HEADER pHeader;
1434 CACHEFILE_ENTRY * pEntry;
1435 URL_CACHEFILE_ENTRY * pUrlEntry;
1436 URLCACHECONTAINER * pContainer;
1437 LPSTR lpszUrlA;
1438 int url_len;
1440 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1442 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1443 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1444 if (!lpszUrlA)
1446 SetLastError(ERROR_OUTOFMEMORY);
1447 return FALSE;
1449 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1451 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1453 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1454 return FALSE;
1457 if (!URLCacheContainer_OpenIndex(pContainer))
1459 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1460 return FALSE;
1463 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1465 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1466 return FALSE;
1469 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1471 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1472 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1473 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1474 SetLastError(ERROR_FILE_NOT_FOUND);
1475 return FALSE;
1477 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1479 if (pEntry->dwSignature != URL_SIGNATURE)
1481 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1482 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1483 SetLastError(ERROR_FILE_NOT_FOUND);
1484 return FALSE;
1487 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1488 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1489 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1491 if (!URLCache_CopyEntry(
1492 pContainer,
1493 pHeader,
1494 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1495 lpdwCacheEntryInfoBufferSize,
1496 pUrlEntry,
1497 TRUE /* UNICODE */))
1499 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1500 return FALSE;
1502 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1504 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1506 return TRUE;
1509 /***********************************************************************
1510 * GetUrlCacheEntryInfoExW (WININET.@)
1513 BOOL WINAPI GetUrlCacheEntryInfoExW(
1514 LPCWSTR lpszUrl,
1515 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1516 LPDWORD lpdwCacheEntryInfoBufSize,
1517 LPWSTR lpszReserved,
1518 LPDWORD lpdwReserved,
1519 LPVOID lpReserved,
1520 DWORD dwFlags)
1522 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1523 debugstr_w(lpszUrl),
1524 lpCacheEntryInfo,
1525 lpdwCacheEntryInfoBufSize,
1526 lpszReserved,
1527 lpdwReserved,
1528 lpReserved,
1529 dwFlags);
1531 if ((lpszReserved != NULL) ||
1532 (lpdwReserved != NULL) ||
1533 (lpReserved != NULL))
1535 ERR("Reserved value was not 0\n");
1536 SetLastError(ERROR_INVALID_PARAMETER);
1537 return FALSE;
1539 if (dwFlags != 0)
1540 FIXME("Undocumented flag(s): %lx\n", dwFlags);
1541 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1544 /***********************************************************************
1545 * SetUrlCacheEntryInfoA (WININET.@)
1547 BOOL WINAPI SetUrlCacheEntryInfoA(
1548 LPCSTR lpszUrlName,
1549 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1550 DWORD dwFieldControl)
1552 LPURLCACHE_HEADER pHeader;
1553 CACHEFILE_ENTRY * pEntry;
1554 URLCACHECONTAINER * pContainer;
1556 TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1558 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1559 return FALSE;
1561 if (!URLCacheContainer_OpenIndex(pContainer))
1562 return FALSE;
1564 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1565 return FALSE;
1567 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1569 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1570 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1571 SetLastError(ERROR_FILE_NOT_FOUND);
1572 return FALSE;
1575 if (pEntry->dwSignature != URL_SIGNATURE)
1577 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1578 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1579 SetLastError(ERROR_FILE_NOT_FOUND);
1580 return FALSE;
1583 URLCache_SetEntryInfo(
1584 (URL_CACHEFILE_ENTRY *)pEntry,
1585 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1586 dwFieldControl);
1588 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1590 return TRUE;
1593 /***********************************************************************
1594 * SetUrlCacheEntryInfoW (WININET.@)
1596 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1598 LPURLCACHE_HEADER pHeader;
1599 CACHEFILE_ENTRY * pEntry;
1600 URLCACHECONTAINER * pContainer;
1601 LPSTR lpszUrlA;
1602 int url_len;
1604 TRACE("(%s, %p, 0x%08lx)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1606 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1607 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1608 if (!lpszUrlA)
1610 SetLastError(ERROR_OUTOFMEMORY);
1611 return FALSE;
1613 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1615 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1617 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1618 return FALSE;
1621 if (!URLCacheContainer_OpenIndex(pContainer))
1623 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1624 return FALSE;
1627 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1629 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1630 return FALSE;
1633 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1635 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1636 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1637 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1638 SetLastError(ERROR_FILE_NOT_FOUND);
1639 return FALSE;
1641 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1643 if (pEntry->dwSignature != URL_SIGNATURE)
1645 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1646 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1647 SetLastError(ERROR_FILE_NOT_FOUND);
1648 return FALSE;
1651 URLCache_SetEntryInfo(
1652 (URL_CACHEFILE_ENTRY *)pEntry,
1653 lpCacheEntryInfo,
1654 dwFieldControl);
1656 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1658 return TRUE;
1661 /***********************************************************************
1662 * RetrieveUrlCacheEntryFileA (WININET.@)
1665 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1666 IN LPCSTR lpszUrlName,
1667 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1668 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1669 IN DWORD dwReserved
1672 LPURLCACHE_HEADER pHeader;
1673 CACHEFILE_ENTRY * pEntry;
1674 URL_CACHEFILE_ENTRY * pUrlEntry;
1675 URLCACHECONTAINER * pContainer;
1677 TRACE("(%s, %p, %p, 0x%08lx)\n",
1678 debugstr_a(lpszUrlName),
1679 lpCacheEntryInfo,
1680 lpdwCacheEntryInfoBufferSize,
1681 dwReserved);
1683 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1684 return FALSE;
1686 if (!URLCacheContainer_OpenIndex(pContainer))
1687 return FALSE;
1689 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1690 return FALSE;
1692 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1694 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1695 TRACE("entry %s not found!\n", lpszUrlName);
1696 SetLastError(ERROR_FILE_NOT_FOUND);
1697 return FALSE;
1700 if (pEntry->dwSignature != URL_SIGNATURE)
1702 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1703 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1704 SetLastError(ERROR_FILE_NOT_FOUND);
1705 return FALSE;
1708 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1709 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1710 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1712 pUrlEntry->dwHitRate++;
1713 pUrlEntry->dwUseCount++;
1714 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1716 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1718 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1719 return FALSE;
1721 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1723 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1725 return TRUE;
1728 /***********************************************************************
1729 * RetrieveUrlCacheEntryFileW (WININET.@)
1732 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1733 IN LPCWSTR lpszUrlName,
1734 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1735 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1736 IN DWORD dwReserved
1739 TRACE("(%s, %p, %p, 0x%08lx)\n",
1740 debugstr_w(lpszUrlName),
1741 lpCacheEntryInfo,
1742 lpdwCacheEntryInfoBufferSize,
1743 dwReserved);
1745 return FALSE;
1748 /***********************************************************************
1749 * UnlockUrlCacheEntryFileA (WININET.@)
1752 BOOL WINAPI UnlockUrlCacheEntryFileA(
1753 IN LPCSTR lpszUrlName,
1754 IN DWORD dwReserved
1757 LPURLCACHE_HEADER pHeader;
1758 CACHEFILE_ENTRY * pEntry;
1759 URL_CACHEFILE_ENTRY * pUrlEntry;
1760 URLCACHECONTAINER * pContainer;
1762 TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName), dwReserved);
1764 if (dwReserved)
1766 ERR("dwReserved != 0\n");
1767 SetLastError(ERROR_INVALID_PARAMETER);
1768 return FALSE;
1771 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1772 return FALSE;
1774 if (!URLCacheContainer_OpenIndex(pContainer))
1775 return FALSE;
1777 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1778 return FALSE;
1780 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1782 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1783 TRACE("entry %s not found!\n", lpszUrlName);
1784 SetLastError(ERROR_FILE_NOT_FOUND);
1785 return FALSE;
1788 if (pEntry->dwSignature != URL_SIGNATURE)
1790 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1791 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1792 SetLastError(ERROR_FILE_NOT_FOUND);
1793 return FALSE;
1796 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1798 if (pUrlEntry->dwUseCount == 0)
1800 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1801 return FALSE;
1803 pUrlEntry->dwUseCount--;
1804 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1806 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1808 return TRUE;
1811 /***********************************************************************
1812 * UnlockUrlCacheEntryFileW (WININET.@)
1815 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1817 FIXME("(%s, 0x%08lx)\n", debugstr_w(lpszUrlName), dwReserved);
1818 return TRUE;
1821 /***********************************************************************
1822 * CreateUrlCacheEntryA (WININET.@)
1825 BOOL WINAPI CreateUrlCacheEntryA(
1826 IN LPCSTR lpszUrlName,
1827 IN DWORD dwExpectedFileSize,
1828 IN LPCSTR lpszFileExtension,
1829 OUT LPSTR lpszFileName,
1830 IN DWORD dwReserved
1833 DWORD len;
1834 WCHAR *url_name;
1835 WCHAR *file_extension;
1836 WCHAR file_name[MAX_PATH];
1837 BOOL bSuccess = FALSE;
1838 DWORD dwError = 0;
1840 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
1841 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1843 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
1844 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
1845 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1847 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
1848 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
1850 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
1852 bSuccess = TRUE;
1854 else
1856 dwError = GetLastError();
1859 else
1861 dwError = GetLastError();
1863 HeapFree(GetProcessHeap(), 0, file_extension);
1865 else
1867 dwError = GetLastError();
1869 HeapFree(GetProcessHeap(), 0, url_name);
1870 if (!bSuccess)
1871 SetLastError(dwError);
1873 return bSuccess;
1875 /***********************************************************************
1876 * CreateUrlCacheEntryW (WININET.@)
1879 BOOL WINAPI CreateUrlCacheEntryW(
1880 IN LPCWSTR lpszUrlName,
1881 IN DWORD dwExpectedFileSize,
1882 IN LPCWSTR lpszFileExtension,
1883 OUT LPWSTR lpszFileName,
1884 IN DWORD dwReserved
1887 URLCACHECONTAINER * pContainer;
1888 LPURLCACHE_HEADER pHeader;
1889 CHAR szFile[MAX_PATH];
1890 WCHAR szExtension[MAX_PATH];
1891 LPCWSTR lpszUrlPart;
1892 LPCWSTR lpszUrlEnd;
1893 LPCWSTR lpszFileNameExtension;
1894 LPWSTR lpszFileNameNoPath;
1895 int i;
1896 int countnoextension;
1897 BYTE CacheDir;
1898 LONG lBufferSize;
1899 BOOL bFound = FALSE;
1900 int count;
1901 static const WCHAR szWWW[] = {'w','w','w',0};
1903 TRACE("(%s, 0x%08lx, %s, %p, 0x%08lx)\n",
1904 debugstr_w(lpszUrlName),
1905 dwExpectedFileSize,
1906 debugstr_w(lpszFileExtension),
1907 lpszFileName,
1908 dwReserved);
1910 if (dwReserved)
1912 ERR("dwReserved != 0\n");
1913 SetLastError(ERROR_INVALID_PARAMETER);
1914 return FALSE;
1917 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1920 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1921 lpszUrlEnd--;
1923 for (lpszUrlPart = lpszUrlEnd;
1924 (lpszUrlPart >= lpszUrlName);
1925 lpszUrlPart--)
1927 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1929 bFound = TRUE;
1930 lpszUrlPart++;
1931 break;
1934 if (!lstrcmpW(lpszUrlPart, szWWW))
1936 lpszUrlPart += lstrlenW(szWWW);
1939 count = lpszUrlEnd - lpszUrlPart;
1941 if (bFound && (count < MAX_PATH))
1943 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
1944 if (!len)
1945 return FALSE;
1946 szFile[len] = '\0';
1947 /* FIXME: get rid of illegal characters like \, / and : */
1949 else
1951 FIXME("need to generate a random filename\n");
1954 TRACE("File name: %s\n", debugstr_a(szFile));
1956 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1957 return FALSE;
1959 if (!URLCacheContainer_OpenIndex(pContainer))
1960 return FALSE;
1962 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1963 return FALSE;
1965 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1967 lBufferSize = MAX_PATH * sizeof(WCHAR);
1968 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1970 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1972 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
1973 lpszFileNameNoPath >= lpszFileName;
1974 --lpszFileNameNoPath)
1976 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
1977 break;
1980 countnoextension = lstrlenW(lpszFileNameNoPath);
1981 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
1982 if (lpszFileNameExtension)
1983 countnoextension -= lstrlenW(lpszFileNameExtension);
1984 *szExtension = '\0';
1986 if (lpszFileExtension)
1988 szExtension[0] = '.';
1989 lstrcpyW(szExtension+1, lpszFileExtension);
1992 for (i = 0; i < 255; i++)
1994 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
1995 HANDLE hFile;
1996 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
1997 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
1998 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1999 if (hFile != INVALID_HANDLE_VALUE)
2001 CloseHandle(hFile);
2002 return TRUE;
2006 return FALSE;
2010 /***********************************************************************
2011 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2013 * The bug we are compensating for is that some drongo at Microsoft
2014 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2015 * As a consequence, CommitUrlCacheEntryA has been effectively
2016 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2017 * is still defined as LPCWSTR. The result (other than madness) is
2018 * that we always need to store lpHeaderInfo in CP_ACP rather than
2019 * in UTF16, and we need to avoid converting lpHeaderInfo in
2020 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2021 * result will lose data for arbitrary binary data.
2024 static BOOL WINAPI CommitUrlCacheEntryInternal(
2025 IN LPCWSTR lpszUrlName,
2026 IN LPCWSTR lpszLocalFileName,
2027 IN FILETIME ExpireTime,
2028 IN FILETIME LastModifiedTime,
2029 IN DWORD CacheEntryType,
2030 IN LPBYTE lpHeaderInfo,
2031 IN DWORD dwHeaderSize,
2032 IN LPCWSTR lpszFileExtension,
2033 IN LPCWSTR lpszOriginalUrl
2036 URLCACHECONTAINER * pContainer;
2037 LPURLCACHE_HEADER pHeader;
2038 CACHEFILE_ENTRY * pEntry;
2039 URL_CACHEFILE_ENTRY * pUrlEntry;
2040 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2041 DWORD dwOffsetLocalFileName = 0;
2042 DWORD dwOffsetHeader = 0;
2043 DWORD dwFileSizeLow = 0;
2044 DWORD dwFileSizeHigh = 0;
2045 BYTE cDirectory = 0;
2046 char achFile[MAX_PATH];
2047 char achUrl[MAX_PATH];
2048 char *pchLocalFileName = 0;
2050 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n",
2051 debugstr_w(lpszUrlName),
2052 debugstr_w(lpszLocalFileName),
2053 CacheEntryType,
2054 lpHeaderInfo,
2055 dwHeaderSize,
2056 debugstr_w(lpszFileExtension),
2057 debugstr_w(lpszOriginalUrl));
2059 if (lpszOriginalUrl)
2060 WARN(": lpszOriginalUrl ignored\n");
2062 if (lpszLocalFileName)
2064 HANDLE hFile;
2066 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2067 if (hFile == INVALID_HANDLE_VALUE)
2069 ERR("couldn't open file %s (error is %ld)\n", debugstr_w(lpszLocalFileName), GetLastError());
2070 return FALSE;
2073 /* Get file size */
2074 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2075 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2077 ERR("couldn't get file size (error is %ld)\n", GetLastError());
2078 CloseHandle(hFile);
2079 return FALSE;
2082 CloseHandle(hFile);
2085 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2086 return FALSE;
2088 if (!URLCacheContainer_OpenIndex(pContainer))
2089 return FALSE;
2091 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2092 return FALSE;
2094 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
2096 if (URLCache_FindEntryInHash(pHeader, achUrl, &pEntry))
2098 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2099 FIXME("entry already in cache - don't know what to do!\n");
2101 * SetLastError(ERROR_FILE_NOT_FOUND);
2102 * return FALSE;
2104 return TRUE;
2107 if (lpszLocalFileName)
2109 BOOL bFound = FALSE;
2111 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2113 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2114 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2115 SetLastError(ERROR_INVALID_PARAMETER);
2116 return FALSE;
2119 /* skip container path prefix */
2120 lpszLocalFileName += lstrlenW(pContainer->path);
2122 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2123 pchLocalFileName = achFile;
2125 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2127 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2129 bFound = TRUE;
2130 break;
2134 if (!bFound)
2136 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2137 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2138 SetLastError(ERROR_INVALID_PARAMETER);
2139 return FALSE;
2142 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2145 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
2146 if (lpszLocalFileName)
2148 dwOffsetLocalFileName = dwBytesNeeded;
2149 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2151 if (lpHeaderInfo)
2153 dwOffsetHeader = dwBytesNeeded;
2154 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2157 /* round up to next block */
2158 if (dwBytesNeeded % BLOCKSIZE)
2160 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2161 dwBytesNeeded += BLOCKSIZE;
2164 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2166 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2167 ERR("no free entries\n");
2168 SetLastError(ERROR_DISK_FULL);
2169 return FALSE;
2172 /* FindFirstFreeEntry fills in blocks used */
2173 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2174 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2175 pUrlEntry->CacheDir = cDirectory;
2176 pUrlEntry->CacheEntryType = CacheEntryType;
2177 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2178 pUrlEntry->dwExemptDelta = 0;
2179 pUrlEntry->dwHitRate = 0;
2180 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2181 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2182 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2183 pUrlEntry->dwSizeHigh = 0;
2184 pUrlEntry->dwSizeLow = dwFileSizeLow;
2185 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2186 pUrlEntry->dwUseCount = 0;
2187 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2188 pUrlEntry->LastModifiedTime = LastModifiedTime;
2189 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2190 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2191 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2192 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2194 /*** Unknowns ***/
2195 pUrlEntry->dwUnknown1 = 0;
2196 pUrlEntry->dwUnknown2 = 0;
2197 pUrlEntry->dwUnknown3 = 0x60;
2198 pUrlEntry->Unknown4 = 0;
2199 pUrlEntry->wUnknown5 = 0x1010;
2200 pUrlEntry->dwUnknown6 = 0;
2201 pUrlEntry->dwUnknown7 = 0;
2202 pUrlEntry->dwUnknown8 = 0;
2205 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, achUrl);
2206 if (dwOffsetLocalFileName)
2207 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2208 if (dwOffsetHeader)
2209 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2211 if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2213 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2214 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2215 return FALSE;
2218 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2220 return TRUE;
2223 /***********************************************************************
2224 * CommitUrlCacheEntryA (WININET.@)
2227 BOOL WINAPI CommitUrlCacheEntryA(
2228 IN LPCSTR lpszUrlName,
2229 IN LPCSTR lpszLocalFileName,
2230 IN FILETIME ExpireTime,
2231 IN FILETIME LastModifiedTime,
2232 IN DWORD CacheEntryType,
2233 IN LPBYTE lpHeaderInfo,
2234 IN DWORD dwHeaderSize,
2235 IN LPCSTR lpszFileExtension,
2236 IN LPCSTR lpszOriginalUrl
2239 DWORD len;
2240 WCHAR *url_name;
2241 WCHAR *local_file_name;
2242 WCHAR *original_url = NULL;
2243 BOOL bSuccess = FALSE;
2244 DWORD dwError = 0;
2246 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n",
2247 debugstr_a(lpszUrlName),
2248 debugstr_a(lpszLocalFileName),
2249 CacheEntryType,
2250 lpHeaderInfo,
2251 dwHeaderSize,
2252 debugstr_a(lpszFileExtension),
2253 debugstr_a(lpszOriginalUrl));
2255 if (lpszFileExtension != 0)
2257 SetLastError(ERROR_INVALID_PARAMETER);
2258 return FALSE;
2260 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2261 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2263 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2264 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2265 (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2267 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2268 if (!lpszOriginalUrl ||
2269 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2270 (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2272 if (original_url)
2273 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2274 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2275 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2276 NULL, original_url))
2278 bSuccess = TRUE;
2280 else
2282 dwError = GetLastError();
2284 HeapFree(GetProcessHeap(), 0, original_url);
2286 else
2288 dwError = GetLastError();
2290 HeapFree(GetProcessHeap(), 0, local_file_name);
2292 else
2294 dwError = GetLastError();
2296 HeapFree(GetProcessHeap(), 0, url_name);
2297 if (!bSuccess)
2298 SetLastError(dwError);
2300 return bSuccess;
2303 /***********************************************************************
2304 * CommitUrlCacheEntryW (WININET.@)
2307 BOOL WINAPI CommitUrlCacheEntryW(
2308 IN LPCWSTR lpszUrlName,
2309 IN LPCWSTR lpszLocalFileName,
2310 IN FILETIME ExpireTime,
2311 IN FILETIME LastModifiedTime,
2312 IN DWORD CacheEntryType,
2313 IN LPWSTR lpHeaderInfo,
2314 IN DWORD dwHeaderSize,
2315 IN LPCWSTR lpszFileExtension,
2316 IN LPCWSTR lpszOriginalUrl
2319 DWORD dwError = 0;
2320 BOOL bSuccess = FALSE;
2321 DWORD len = 0;
2322 CHAR *header_info = NULL;
2324 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n",
2325 debugstr_w(lpszUrlName),
2326 debugstr_w(lpszLocalFileName),
2327 CacheEntryType,
2328 lpHeaderInfo,
2329 dwHeaderSize,
2330 debugstr_w(lpszFileExtension),
2331 debugstr_w(lpszOriginalUrl));
2333 if (!lpHeaderInfo ||
2334 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2335 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2337 if (header_info)
2338 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2339 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2340 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2342 bSuccess = TRUE;
2344 else
2346 dwError = GetLastError();
2348 if (header_info)
2350 HeapFree(GetProcessHeap(), 0, header_info);
2351 if (!bSuccess)
2352 SetLastError(dwError);
2355 return bSuccess;
2358 /***********************************************************************
2359 * ReadUrlCacheEntryStream (WININET.@)
2362 BOOL WINAPI ReadUrlCacheEntryStream(
2363 IN HANDLE hUrlCacheStream,
2364 IN DWORD dwLocation,
2365 IN OUT LPVOID lpBuffer,
2366 IN OUT LPDWORD lpdwLen,
2367 IN DWORD dwReserved
2370 /* Get handle to file from 'stream' */
2371 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2373 if (dwReserved != 0)
2375 ERR("dwReserved != 0\n");
2376 SetLastError(ERROR_INVALID_PARAMETER);
2377 return FALSE;
2380 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2382 SetLastError(ERROR_INVALID_HANDLE);
2383 return FALSE;
2386 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2387 return FALSE;
2388 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2391 /***********************************************************************
2392 * RetrieveUrlCacheEntryStreamA (WININET.@)
2395 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2396 IN LPCSTR lpszUrlName,
2397 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2398 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2399 IN BOOL fRandomRead,
2400 IN DWORD dwReserved
2403 /* NOTE: this is not the same as the way that the native
2404 * version allocates 'stream' handles. I did it this way
2405 * as it is much easier and no applications should depend
2406 * on this behaviour. (Native version appears to allocate
2407 * indices into a table)
2409 STREAM_HANDLE * pStream;
2410 HANDLE hFile;
2412 TRACE( "(%s, %p, %p, %x, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2413 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2415 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2416 lpCacheEntryInfo,
2417 lpdwCacheEntryInfoBufferSize,
2418 dwReserved))
2420 return NULL;
2423 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2424 GENERIC_READ,
2425 FILE_SHARE_READ,
2426 NULL,
2427 OPEN_EXISTING,
2428 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2429 NULL);
2430 if (hFile == INVALID_HANDLE_VALUE)
2431 return FALSE;
2433 /* allocate handle storage space */
2434 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2435 if (!pStream)
2437 CloseHandle(hFile);
2438 SetLastError(ERROR_OUTOFMEMORY);
2439 return FALSE;
2442 pStream->hFile = hFile;
2443 strcpy(pStream->lpszUrl, lpszUrlName);
2444 return (HANDLE)pStream;
2447 /***********************************************************************
2448 * RetrieveUrlCacheEntryStreamW (WININET.@)
2451 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2452 IN LPCWSTR lpszUrlName,
2453 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2454 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2455 IN BOOL fRandomRead,
2456 IN DWORD dwReserved
2459 FIXME( "(%s, %p, %p, %x, 0x%08lx)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2460 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2461 return NULL;
2464 /***********************************************************************
2465 * UnlockUrlCacheEntryStream (WININET.@)
2468 BOOL WINAPI UnlockUrlCacheEntryStream(
2469 IN HANDLE hUrlCacheStream,
2470 IN DWORD dwReserved
2473 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2475 if (dwReserved != 0)
2477 ERR("dwReserved != 0\n");
2478 SetLastError(ERROR_INVALID_PARAMETER);
2479 return FALSE;
2482 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2484 SetLastError(ERROR_INVALID_HANDLE);
2485 return FALSE;
2488 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2489 return FALSE;
2491 /* close file handle */
2492 CloseHandle(pStream->hFile);
2494 /* free allocated space */
2495 HeapFree(GetProcessHeap(), 0, pStream);
2497 return TRUE;
2501 /***********************************************************************
2502 * DeleteUrlCacheEntryA (WININET.@)
2505 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2507 URLCACHECONTAINER * pContainer;
2508 LPURLCACHE_HEADER pHeader;
2509 CACHEFILE_ENTRY * pEntry;
2511 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2513 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2514 return FALSE;
2516 if (!URLCacheContainer_OpenIndex(pContainer))
2517 return FALSE;
2519 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2520 return FALSE;
2522 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2524 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2525 TRACE("entry %s not found!\n", lpszUrlName);
2526 SetLastError(ERROR_FILE_NOT_FOUND);
2527 return FALSE;
2530 URLCache_DeleteEntry(pHeader, pEntry);
2532 URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2534 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2536 return TRUE;
2539 /***********************************************************************
2540 * DeleteUrlCacheEntryW (WININET.@)
2543 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2545 FIXME("(%s) stub\n", debugstr_w(lpszUrlName));
2546 return TRUE;
2549 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2551 FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
2552 return TRUE;
2555 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2557 FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
2558 return TRUE;
2561 /***********************************************************************
2562 * CreateCacheContainerA (WININET.@)
2564 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2565 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2567 FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
2568 d1, d2, d3, d4, d5, d6, d7, d8);
2569 return TRUE;
2572 /***********************************************************************
2573 * CreateCacheContainerW (WININET.@)
2575 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2576 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2578 FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
2579 d1, d2, d3, d4, d5, d6, d7, d8);
2580 return TRUE;
2583 /***********************************************************************
2584 * FindCloseUrlCache (WININET.@)
2586 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2588 FIXME("(%p) stub\n", hEnumHandle);
2589 return TRUE;
2592 /***********************************************************************
2593 * FindFirstUrlCacheContainerA (WININET.@)
2595 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2597 FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
2598 return NULL;
2601 /***********************************************************************
2602 * FindFirstUrlCacheContainerW (WININET.@)
2604 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2606 FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
2607 return NULL;
2610 /***********************************************************************
2611 * FindNextUrlCacheContainerA (WININET.@)
2613 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2615 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2616 return FALSE;
2619 /***********************************************************************
2620 * FindNextUrlCacheContainerW (WININET.@)
2622 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2624 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2625 return FALSE;
2628 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2629 LPCSTR lpszUrlSearchPattern,
2630 DWORD dwFlags,
2631 DWORD dwFilter,
2632 GROUPID GroupId,
2633 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2634 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2635 LPVOID lpReserved,
2636 LPDWORD pcbReserved2,
2637 LPVOID lpReserved3
2640 FIXME("(%s, 0x%08lx, 0x%08lx, 0x%08lx%08lx, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2641 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2642 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2643 SetLastError(ERROR_FILE_NOT_FOUND);
2644 return NULL;
2647 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2648 LPCWSTR lpszUrlSearchPattern,
2649 DWORD dwFlags,
2650 DWORD dwFilter,
2651 GROUPID GroupId,
2652 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2653 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2654 LPVOID lpReserved,
2655 LPDWORD pcbReserved2,
2656 LPVOID lpReserved3
2659 FIXME("(%s, 0x%08lx, 0x%08lx, 0x%08lx%08lx, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2660 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2661 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2662 SetLastError(ERROR_FILE_NOT_FOUND);
2663 return NULL;
2666 /***********************************************************************
2667 * FindFirstUrlCacheEntryA (WININET.@)
2670 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2671 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2673 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2674 SetLastError(ERROR_FILE_NOT_FOUND);
2675 return 0;
2678 /***********************************************************************
2679 * FindFirstUrlCacheEntryW (WININET.@)
2682 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2683 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2685 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2686 return 0;
2689 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2690 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2692 FIXME("(0x%08lx, 0x%08lx, %p, 0x%08lx, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2693 dwSearchCondition, lpGroupId, lpReserved);
2694 return NULL;
2697 BOOL WINAPI FindNextUrlCacheEntryA(
2698 HANDLE hEnumHandle,
2699 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2700 LPDWORD lpdwNextCacheEntryInfoBufferSize
2703 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2704 return FALSE;
2707 BOOL WINAPI FindNextUrlCacheEntryW(
2708 HANDLE hEnumHandle,
2709 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2710 LPDWORD lpdwNextCacheEntryInfoBufferSize
2713 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2714 return FALSE;
2717 BOOL WINAPI FindNextUrlCacheEntryExA(
2718 HANDLE hEnumHandle,
2719 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2720 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2721 LPVOID lpReserved,
2722 LPDWORD pcbReserved2,
2723 LPVOID lpReserved3
2726 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2727 lpReserved, pcbReserved2, lpReserved3);
2728 return FALSE;
2731 BOOL WINAPI FindNextUrlCacheEntryExW(
2732 HANDLE hEnumHandle,
2733 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2734 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2735 LPVOID lpReserved,
2736 LPDWORD pcbReserved2,
2737 LPVOID lpReserved3
2740 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2741 lpReserved, pcbReserved2, lpReserved3);
2742 return FALSE;
2745 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2747 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2748 return FALSE;
2751 /***********************************************************************
2752 * CreateUrlCacheGroup (WININET.@)
2755 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2757 FIXME("(0x%08lx, %p): stub\n", dwFlags, lpReserved);
2758 return FALSE;
2761 /***********************************************************************
2762 * DeleteUrlCacheGroup (WININET.@)
2765 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2767 FIXME("(0x%08lx%08lx, 0x%08lx, %p) stub\n",
2768 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2769 return FALSE;
2772 /***********************************************************************
2773 * SetUrlCacheEntryGroupA (WININET.@)
2776 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2777 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2778 LPVOID lpReserved)
2780 FIXME("(%s, 0x%08lx, 0x%08lx%08lx, %p, 0x%08lx, %p) stub\n",
2781 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2782 pbGroupAttributes, cbGroupAttributes, lpReserved);
2783 SetLastError(ERROR_FILE_NOT_FOUND);
2784 return FALSE;
2787 /***********************************************************************
2788 * SetUrlCacheEntryGroupW (WININET.@)
2791 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2792 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2793 LPVOID lpReserved)
2795 FIXME("(%s, 0x%08lx, 0x%08lx%08lx, %p, 0x%08lx, %p) stub\n",
2796 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2797 pbGroupAttributes, cbGroupAttributes, lpReserved);
2798 SetLastError(ERROR_FILE_NOT_FOUND);
2799 return FALSE;
2802 /***********************************************************************
2803 * GetUrlCacheConfigInfoW (WININET.@)
2805 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2807 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2808 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2809 return FALSE;
2812 /***********************************************************************
2813 * GetUrlCacheConfigInfoA (WININET.@)
2815 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2817 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2819 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2820 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2821 return FALSE;
2824 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2825 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2826 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2828 FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
2829 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2830 lpdwGroupInfo, lpReserved);
2831 return FALSE;
2834 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2835 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2836 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2838 FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
2839 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2840 lpdwGroupInfo, lpReserved);
2841 return FALSE;
2844 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2845 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2847 FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p) stub\n",
2848 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2849 return TRUE;
2852 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2853 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2855 FIXME("(0x%08lx%08lx, 0x%08lx, 0x%08lx, %p, %p) stub\n",
2856 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2857 return TRUE;
2860 BOOL WINAPI SetUrlCacheConfigInfoA( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2862 FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
2863 return TRUE;
2866 BOOL WINAPI SetUrlCacheConfigInfoW( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2868 FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
2869 return TRUE;
2872 /***********************************************************************
2873 * DeleteIE3Cache (WININET.@)
2875 * Deletes the files used by the IE3 URL caching system.
2877 * PARAMS
2878 * hWnd [I] A dummy window.
2879 * hInst [I] Instance of process calling the function.
2880 * lpszCmdLine [I] Options used by function.
2881 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
2883 * RETURNS
2884 * nothing
2886 void WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2888 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);