2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
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
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
64 static const char urlcache_ver[] = "0.2012001";
66 #define ENTRY_START_OFFSET 0x4000
68 #define MAX_DIR_NO 0x20
70 #define HASHTABLE_SIZE 448
71 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
72 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
73 #define ALLOCATION_TABLE_OFFSET 0x250
74 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
75 #define MIN_BLOCK_NO 0x80
76 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
77 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
79 #define HASHTABLE_URL 0
80 #define HASHTABLE_DEL 1
81 #define HASHTABLE_LOCK 2
82 #define HASHTABLE_FREE 3
83 #define HASHTABLE_REDR 5
84 #define HASHTABLE_FLAG_BITS 6
86 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
87 #define INSTALLED_CACHE_ENTRY 0x10000000
88 #define GET_INSTALLED_ENTRY 0x200
89 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
91 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
93 #define FILETIME_SECOND 10000000
95 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
96 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
97 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
98 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
99 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
101 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
103 typedef struct _CACHEFILE_ENTRY
107 DWORD dwSignature; /* e.g. "URL " */
108 /* CHAR szSignature[4];
110 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
113 typedef struct _URL_CACHEFILE_ENTRY
115 CACHEFILE_ENTRY CacheFileEntry;
116 FILETIME LastModifiedTime;
117 FILETIME LastAccessTime;
118 WORD wExpiredDate; /* expire date in dos format */
119 WORD wExpiredTime; /* expire time in dos format */
120 DWORD dwUnknown1; /* usually zero */
121 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
122 DWORD dwUnknown2; /* usually zero */
123 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
124 DWORD dwUnknown3; /* usually 0x60 */
125 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
126 BYTE CacheDir; /* index of cache directory this url is stored in */
127 BYTE Unknown4; /* usually zero */
128 WORD wUnknown5; /* usually 0x1010 */
129 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
130 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
131 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
132 DWORD dwHeaderInfoSize;
133 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
134 WORD wLastSyncDate; /* last sync date in dos format */
135 WORD wLastSyncTime; /* last sync time in dos format */
136 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
137 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
140 DWORD dwUnknown7; /* usually zero */
141 DWORD dwUnknown8; /* usually zero */
142 /* packing to dword align start of next field */
143 /* CHAR szSourceUrlName[]; (url) */
144 /* packing to dword align start of next field */
145 /* CHAR szLocalFileName[]; (local file name excluding path) */
146 /* packing to dword align start of next field */
147 /* CHAR szHeaderInfo[]; (header info) */
148 } URL_CACHEFILE_ENTRY;
156 typedef struct _HASH_CACHEFILE_ENTRY
158 CACHEFILE_ENTRY CacheFileEntry;
160 DWORD dwHashTableNumber;
161 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
162 } HASH_CACHEFILE_ENTRY;
164 typedef struct _DIRECTORY_DATA
167 char filename[DIR_LENGTH];
170 typedef struct _URLCACHE_HEADER
172 char szSignature[28];
174 DWORD dwOffsetFirstHashTable;
175 DWORD dwIndexCapacityInBlocks;
178 ULARGE_INTEGER CacheLimit;
179 ULARGE_INTEGER CacheUsage;
180 ULARGE_INTEGER ExemptUsage;
181 DWORD DirectoryCount;
182 DIRECTORY_DATA directory_data[MAX_DIR_NO];
184 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
185 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
186 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
188 typedef struct _STREAM_HANDLE
194 typedef struct _URLCACHECONTAINER
196 struct list entry; /* part of a list */
197 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
198 LPWSTR path; /* path to url container directory */
199 HANDLE hMapping; /* handle of file mapping */
200 DWORD file_size; /* size of file when mapping was opened */
201 HANDLE hMutex; /* handle of mutex */
202 DWORD default_entry_type;
206 /* List of all containers available */
207 static struct list UrlContainers = LIST_INIT(UrlContainers);
209 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
211 /***********************************************************************
212 * URLCache_PathToObjectName (Internal)
214 * Converts a path to a name suitable for use as a Win32 object name.
215 * Replaces '\\' characters in-place with the specified character
216 * (usually '_' or '!')
222 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
224 for (; *lpszPath; lpszPath++)
226 if (*lpszPath == '\\')
231 /***********************************************************************
232 * URLCacheContainer_OpenIndex (Internal)
234 * Opens the index file and saves mapping handle in hCacheIndexMapping
237 * ERROR_SUCCESS if succeeded
238 * Any other Win32 error code if failed
241 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer, DWORD blocks_no)
244 WCHAR wszFilePath[MAX_PATH];
245 DWORD dwFileSize, new_file_size;
247 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
248 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
250 WaitForSingleObject(pContainer->hMutex, INFINITE);
252 if (pContainer->hMapping) {
253 ReleaseMutex(pContainer->hMutex);
254 return ERROR_SUCCESS;
257 strcpyW(wszFilePath, pContainer->path);
258 strcatW(wszFilePath, wszIndex);
260 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
261 if (hFile == INVALID_HANDLE_VALUE)
263 /* Maybe the directory wasn't there? Try to create it */
264 if (CreateDirectoryW(pContainer->path, 0))
265 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
267 if (hFile == INVALID_HANDLE_VALUE)
269 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
270 ReleaseMutex(pContainer->hMutex);
271 return GetLastError();
274 dwFileSize = GetFileSize(hFile, NULL);
275 if (dwFileSize == INVALID_FILE_SIZE)
277 ReleaseMutex(pContainer->hMutex);
278 return GetLastError();
281 if (blocks_no < MIN_BLOCK_NO)
282 blocks_no = MIN_BLOCK_NO;
283 else if (blocks_no > MAX_BLOCK_NO)
284 blocks_no = MAX_BLOCK_NO;
285 new_file_size = FILE_SIZE(blocks_no);
287 if (dwFileSize < new_file_size)
289 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
291 char achZeroes[0x1000];
293 DWORD dwError = ERROR_SUCCESS;
295 if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
296 dwError = GetLastError();
298 /* Write zeroes to the entire file so we can safely map it without
299 * fear of getting a SEGV because the disk is full.
301 memset(achZeroes, 0, sizeof(achZeroes));
302 for (dwOffset = dwFileSize; dwOffset<new_file_size && dwError==ERROR_SUCCESS;
303 dwOffset += sizeof(achZeroes))
305 DWORD dwWrite = sizeof(achZeroes);
308 if (new_file_size - dwOffset < dwWrite)
309 dwWrite = new_file_size - dwOffset;
310 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
311 dwWritten != dwWrite)
313 /* If we fail to write, we need to return the error that
314 * cause the problem and also make sure the file is no
315 * longer there, if possible.
317 dwError = GetLastError();
321 if (dwError == ERROR_SUCCESS)
323 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
327 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
329 if (pHeader && dwFileSize)
331 pHeader->dwFileSize = new_file_size;
332 pHeader->dwIndexCapacityInBlocks = blocks_no;
337 WCHAR wszDirPath[MAX_PATH];
340 HASH_CACHEFILE_ENTRY *pHashEntry;
342 /* First set some constants and defaults in the header */
343 memcpy(pHeader->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
344 memcpy(pHeader->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
345 pHeader->dwFileSize = new_file_size;
346 pHeader->dwIndexCapacityInBlocks = blocks_no;
347 /* 127MB - taken from default for Windows 2000 */
348 pHeader->CacheLimit.QuadPart = 0x07ff5400;
349 /* Copied from a Windows 2000 cache index */
350 pHeader->DirectoryCount = pContainer->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
352 /* If the registry has a cache size set, use the registry value */
353 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
356 DWORD len = sizeof(dw);
359 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
360 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
361 keytype == REG_DWORD)
363 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
368 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
370 /* Last step - create the directories */
372 strcpyW(wszDirPath, pContainer->path);
373 pwchDir = wszDirPath + strlenW(wszDirPath);
376 GetSystemTimeAsFileTime(&ft);
378 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
380 pHeader->directory_data[i].dwNumFiles = 0;
384 ULONGLONG n = ft.dwHighDateTime;
386 /* Generate a file name to attempt to create.
387 * This algorithm will create what will appear
388 * to be random and unrelated directory names
389 * of up to 9 characters in length.
392 n += ft.dwLowDateTime;
393 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
395 for (k = 0; k < 8; ++k)
399 /* Dividing by a prime greater than 36 helps
400 * with the appearance of randomness
405 pwchDir[k] = '0' + r;
407 pwchDir[k] = 'A' + (r - 10);
410 if (CreateDirectoryW(wszDirPath, 0))
412 /* The following is OK because we generated an
413 * 8 character directory name made from characters
414 * [A-Z0-9], which are equivalent for all code
415 * pages and for UTF-16
417 for (k = 0; k < 8; ++k)
418 pHeader->directory_data[i].filename[k] = pwchDir[k];
423 /* Give up. The most likely cause of this
424 * is a full disk, but whatever the cause
425 * is, it should be more than apparent that
428 dwError = GetLastError();
434 UnmapViewOfFile(pHeader);
438 dwError = GetLastError();
440 dwFileSize = new_file_size;
441 CloseHandle(hMapping);
445 dwError = GetLastError();
452 DeleteFileW(wszFilePath);
453 ReleaseMutex(pContainer->hMutex);
459 pContainer->file_size = dwFileSize;
460 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
461 URLCache_PathToObjectName(wszFilePath, '_');
462 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
463 if (!pContainer->hMapping)
465 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
468 /* Validate cache index file on first open */
469 if (pContainer->hMapping && blocks_no==MIN_BLOCK_NO)
471 URLCACHE_HEADER *pHeader = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
474 ERR("MapViewOfFile failed (error is %d)\n", GetLastError());
475 CloseHandle(pContainer->hMapping);
476 pContainer->hMapping = NULL;
477 ReleaseMutex(pContainer->hMutex);
478 return GetLastError();
481 if (!memcmp(pHeader->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
482 memcmp(pHeader->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
484 TRACE("detected wrong version of cache: %s, expected %s\n", pHeader->szSignature, urlcache_ver);
485 UnmapViewOfFile(pHeader);
487 FreeUrlCacheSpaceW(pContainer->path, 100, 0);
491 UnmapViewOfFile(pHeader);
499 if (!pContainer->hMapping)
501 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
502 ReleaseMutex(pContainer->hMutex);
503 return GetLastError();
506 ReleaseMutex(pContainer->hMutex);
508 return ERROR_SUCCESS;
511 /***********************************************************************
512 * URLCacheContainer_CloseIndex (Internal)
520 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
522 CloseHandle(pContainer->hMapping);
523 pContainer->hMapping = NULL;
526 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix,
527 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
529 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
530 int cache_prefix_len = strlenW(cache_prefix);
537 pContainer->hMapping = NULL;
538 pContainer->file_size = 0;
539 pContainer->default_entry_type = default_entry_type;
541 pContainer->path = heap_strdupW(path);
542 if (!pContainer->path)
544 heap_free(pContainer);
548 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
549 if (!pContainer->cache_prefix)
551 heap_free(pContainer->path);
552 heap_free(pContainer);
556 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
558 CharLowerW(mutex_name);
559 URLCache_PathToObjectName(mutex_name, '!');
561 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
563 ERR("couldn't create mutex (error is %d)\n", GetLastError());
564 heap_free(pContainer->path);
565 heap_free(pContainer);
569 list_add_head(&UrlContainers, &pContainer->entry);
574 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
576 list_remove(&pContainer->entry);
578 URLCacheContainer_CloseIndex(pContainer);
579 CloseHandle(pContainer->hMutex);
580 heap_free(pContainer->path);
581 heap_free(pContainer->cache_prefix);
582 heap_free(pContainer);
585 static void URLCacheContainers_CreateDefaults(void)
587 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
588 static const WCHAR UrlPrefix[] = {0};
589 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
590 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
591 static const WCHAR CookieSuffix[] = {0};
592 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
595 int nFolder; /* CSIDL_* constant */
596 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
597 const WCHAR * cache_prefix; /* prefix used to reference the container */
598 DWORD default_entry_type;
599 } DefaultContainerData[] =
601 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
602 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
603 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
607 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
609 WCHAR wszCachePath[MAX_PATH];
610 WCHAR wszMutexName[MAX_PATH];
611 int path_len, suffix_len;
613 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
615 ERR("Couldn't get path for default container %u\n", i);
618 path_len = strlenW(wszCachePath);
619 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
621 if (path_len + suffix_len + 2 > MAX_PATH)
623 ERR("Path too long\n");
627 wszCachePath[path_len] = '\\';
628 wszCachePath[path_len+1] = 0;
630 strcpyW(wszMutexName, wszCachePath);
634 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
635 wszCachePath[path_len + suffix_len + 1] = '\\';
636 wszCachePath[path_len + suffix_len + 2] = '\0';
639 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath,
640 DefaultContainerData[i].default_entry_type, wszMutexName);
644 static void URLCacheContainers_DeleteAll(void)
646 while(!list_empty(&UrlContainers))
647 URLCacheContainer_DeleteContainer(
648 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
652 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
654 URLCACHECONTAINER * pContainer;
656 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
659 return ERROR_INVALID_PARAMETER;
661 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
663 int prefix_len = strlenW(pContainer->cache_prefix);
664 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
666 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
667 *ppContainer = pContainer;
668 return ERROR_SUCCESS;
671 ERR("no container found\n");
672 return ERROR_FILE_NOT_FOUND;
675 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
680 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
681 return ERROR_OUTOFMEMORY;
683 ret = URLCacheContainers_FindContainerW(url, ppContainer);
688 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
691 URLCACHECONTAINER * pContainer;
693 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
695 /* non-NULL search pattern only returns one container ever */
696 if (lpwszSearchPattern && dwIndex > 0)
699 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
701 if (lpwszSearchPattern)
703 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
705 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
706 *ppContainer = pContainer;
714 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
715 *ppContainer = pContainer;
724 /***********************************************************************
725 * URLCacheContainer_LockIndex (Internal)
727 * Locks the index for system-wide exclusive access.
730 * Cache file header if successful
731 * NULL if failed and calls SetLastError.
733 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
737 URLCACHE_HEADER * pHeader;
741 WaitForSingleObject(pContainer->hMutex, INFINITE);
743 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
747 ReleaseMutex(pContainer->hMutex);
748 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
751 pHeader = (URLCACHE_HEADER *)pIndexData;
753 /* file has grown - we need to remap to prevent us getting
754 * access violations when we try and access beyond the end
755 * of the memory mapped file */
756 if (pHeader->dwFileSize != pContainer->file_size)
758 UnmapViewOfFile( pHeader );
759 URLCacheContainer_CloseIndex(pContainer);
760 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
761 if (error != ERROR_SUCCESS)
763 ReleaseMutex(pContainer->hMutex);
767 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
771 ReleaseMutex(pContainer->hMutex);
772 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
775 pHeader = (URLCACHE_HEADER *)pIndexData;
778 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
780 for (index = 0; index < pHeader->DirectoryCount; index++)
782 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
788 /***********************************************************************
789 * URLCacheContainer_UnlockIndex (Internal)
792 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
795 ReleaseMutex(pContainer->hMutex);
796 return UnmapViewOfFile(pHeader);
800 #define CHAR_BIT (8 * sizeof(CHAR))
803 /***********************************************************************
804 * URLCache_Allocation_BlockIsFree (Internal)
806 * Is the specified block number free?
813 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
815 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
816 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
819 /***********************************************************************
820 * URLCache_Allocation_BlockFree (Internal)
822 * Marks the specified block as free
825 * this function is not updating used blocks count
831 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
833 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
834 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
837 /***********************************************************************
838 * URLCache_Allocation_BlockAllocate (Internal)
840 * Marks the specified block as allocated
843 * this function is not updating used blocks count
849 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
851 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
852 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
855 /***********************************************************************
856 * URLCache_FindFirstFreeEntry (Internal)
858 * Finds and allocates the first block of free space big enough and
859 * sets ppEntry to point to it.
862 * ERROR_SUCCESS when free memory block was found
863 * Any other Win32 error code if the entry could not be added
866 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
870 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
872 for (dwFreeCounter = 0;
873 dwFreeCounter < dwBlocksNeeded &&
874 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
875 URLCache_Allocation_BlockIsFree(pHeader->allocation_table, dwBlockNumber + dwFreeCounter);
877 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
879 if (dwFreeCounter == dwBlocksNeeded)
882 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
883 for (index = 0; index < dwBlocksNeeded; index++)
884 URLCache_Allocation_BlockAllocate(pHeader->allocation_table, dwBlockNumber + index);
885 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
886 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
887 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
888 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
889 pHeader->dwBlocksInUse += dwBlocksNeeded;
890 return ERROR_SUCCESS;
894 return ERROR_HANDLE_DISK_FULL;
897 /***********************************************************************
898 * URLCache_DeleteEntry (Internal)
900 * Deletes the specified entry and frees the space allocated to it
903 * TRUE if it succeeded
907 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
912 /* update allocation table */
913 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
914 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
915 URLCache_Allocation_BlockFree(pHeader->allocation_table, dwBlock);
917 pHeader->dwBlocksInUse -= pEntry->dwBlocksUsed;
921 /***********************************************************************
922 * URLCache_LocalFileNameToPathW (Internal)
924 * Copies the full path to the specified buffer given the local file
925 * name and the index of the directory it is in. Always sets value in
926 * lpBufferSize to the required buffer size (in bytes).
929 * TRUE if the buffer was big enough
930 * FALSE if the buffer was too small
933 static BOOL URLCache_LocalFileNameToPathW(
934 const URLCACHECONTAINER * pContainer,
935 LPCURLCACHE_HEADER pHeader,
936 LPCSTR szLocalFileName,
942 int path_len = strlenW(pContainer->path);
943 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
944 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
950 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
951 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
952 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
953 if (nRequired <= *lpBufferSize)
957 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
958 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
960 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
961 wszPath[dir_len + path_len] = '\\';
968 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
969 *lpBufferSize = nRequired;
972 *lpBufferSize = nRequired;
976 /***********************************************************************
977 * URLCache_LocalFileNameToPathA (Internal)
979 * Copies the full path to the specified buffer given the local file
980 * name and the index of the directory it is in. Always sets value in
981 * lpBufferSize to the required buffer size.
984 * TRUE if the buffer was big enough
985 * FALSE if the buffer was too small
988 static BOOL URLCache_LocalFileNameToPathA(
989 const URLCACHECONTAINER * pContainer,
990 LPCURLCACHE_HEADER pHeader,
991 LPCSTR szLocalFileName,
997 int path_len, file_name_len, dir_len;
999 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
1005 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1006 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1007 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1008 dir_len = DIR_LENGTH+1;
1012 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1013 if (nRequired < *lpBufferSize)
1015 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1017 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len-1);
1018 szPath[path_len + dir_len-1] = '\\';
1020 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1021 *lpBufferSize = nRequired;
1024 *lpBufferSize = nRequired;
1028 /* Just like FileTimeToDosDateTime, except that it also maps the special
1029 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1031 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1034 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1035 *fatdate = *fattime = 0;
1037 FileTimeToDosDateTime(ft, fatdate, fattime);
1040 /***********************************************************************
1041 * URLCache_DeleteFile (Internal)
1043 static DWORD URLCache_DeleteFile(const URLCACHECONTAINER *container,
1044 URLCACHE_HEADER *header, URL_CACHEFILE_ENTRY *url_entry)
1046 WIN32_FILE_ATTRIBUTE_DATA attr;
1047 WCHAR path[MAX_PATH];
1048 LONG path_size = sizeof(path);
1052 if(!url_entry->dwOffsetLocalName)
1055 if(!URLCache_LocalFileNameToPathW(container, header,
1056 (LPCSTR)url_entry+url_entry->dwOffsetLocalName,
1057 url_entry->CacheDir, path, &path_size))
1060 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1062 URLCache_FileTimeToDosDateTime(&attr.ftLastWriteTime, &date, &time);
1063 if(date != url_entry->LastWriteDate || time != url_entry->LastWriteTime)
1066 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1067 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1071 if (url_entry->CacheDir < header->DirectoryCount)
1073 if (header->directory_data[url_entry->CacheDir].dwNumFiles)
1074 header->directory_data[url_entry->CacheDir].dwNumFiles--;
1076 if (url_entry->CacheEntryType & STICKY_CACHE_ENTRY)
1078 if (url_entry->size.QuadPart < header->ExemptUsage.QuadPart)
1079 header->ExemptUsage.QuadPart -= url_entry->size.QuadPart;
1081 header->ExemptUsage.QuadPart = 0;
1085 if (url_entry->size.QuadPart < header->CacheUsage.QuadPart)
1086 header->CacheUsage.QuadPart -= url_entry->size.QuadPart;
1088 header->CacheUsage.QuadPart = 0;
1091 return ERROR_SUCCESS;
1094 static BOOL urlcache_clean_leaked_entries(URLCACHECONTAINER *container, URLCACHE_HEADER *header)
1099 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1101 URL_CACHEFILE_ENTRY *url_entry = (URL_CACHEFILE_ENTRY*)((LPBYTE)header + *leak_off);
1103 if(SUCCEEDED(URLCache_DeleteFile(container, header, url_entry))) {
1104 *leak_off = url_entry->dwExemptDelta;
1105 URLCache_DeleteEntry(header, &url_entry->CacheFileEntry);
1108 leak_off = &url_entry->dwExemptDelta;
1115 /***********************************************************************
1116 * URLCacheContainer_CleanIndex (Internal)
1118 * This function is meant to make place in index file by removing leaked
1119 * files entries and resizing the file.
1121 * CAUTION: file view may get mapped to new memory
1124 * ERROR_SUCCESS when new memory is available
1125 * error code otherwise
1127 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
1129 URLCACHE_HEADER *header = *file_view;
1132 TRACE("(%s %s)\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
1134 if(urlcache_clean_leaked_entries(container, header))
1135 return ERROR_SUCCESS;
1137 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1138 WARN("index file has maximal size\n");
1139 return ERROR_NOT_ENOUGH_MEMORY;
1142 URLCacheContainer_CloseIndex(container);
1143 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
1144 if(ret != ERROR_SUCCESS)
1146 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
1148 return GetLastError();
1150 UnmapViewOfFile(*file_view);
1151 *file_view = header;
1152 return ERROR_SUCCESS;
1155 /* Just like DosDateTimeToFileTime, except that it also maps the special
1156 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1158 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
1161 if (!fatdate && !fattime)
1162 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1164 DosDateTimeToFileTime(fatdate, fattime, ft);
1167 /***********************************************************************
1168 * URLCache_CopyEntry (Internal)
1170 * Copies an entry from the cache index file to the Win32 structure
1173 * ERROR_SUCCESS if the buffer was big enough
1174 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1177 static DWORD URLCache_CopyEntry(
1178 URLCACHECONTAINER * pContainer,
1179 LPCURLCACHE_HEADER pHeader,
1180 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1181 LPDWORD lpdwBufferSize,
1182 const URL_CACHEFILE_ENTRY * pUrlEntry,
1186 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1188 if (*lpdwBufferSize >= dwRequiredSize)
1190 lpCacheEntryInfo->lpHeaderInfo = NULL;
1191 lpCacheEntryInfo->lpszFileExtension = NULL;
1192 lpCacheEntryInfo->lpszLocalFileName = NULL;
1193 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1194 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1195 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1196 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1197 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1198 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1199 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1200 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1201 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1202 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1203 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1204 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1205 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1206 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1207 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1210 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1211 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1212 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1214 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1216 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1217 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1219 /* FIXME: is source url optional? */
1220 if (*lpdwBufferSize >= dwRequiredSize)
1222 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1224 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1226 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1228 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1231 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1232 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1233 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1235 if (pUrlEntry->dwOffsetLocalName)
1237 LONG nLocalFilePathSize;
1238 LPSTR lpszLocalFileName;
1239 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1240 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1241 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1242 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1244 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1246 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1248 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1249 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1250 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1252 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1254 if (*lpdwBufferSize >= dwRequiredSize)
1256 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1257 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1258 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1260 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1261 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1262 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1264 if (pUrlEntry->dwOffsetFileExtension)
1269 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1271 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1272 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1274 if (*lpdwBufferSize >= dwRequiredSize)
1276 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1278 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1280 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1283 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1284 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1285 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1288 if (dwRequiredSize > *lpdwBufferSize)
1290 *lpdwBufferSize = dwRequiredSize;
1291 return ERROR_INSUFFICIENT_BUFFER;
1293 *lpdwBufferSize = dwRequiredSize;
1294 return ERROR_SUCCESS;
1297 /***********************************************************************
1298 * URLCache_SetEntryInfo (Internal)
1300 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1301 * according to the flags set by dwFieldControl.
1304 * ERROR_SUCCESS if the buffer was big enough
1305 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1308 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1310 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1311 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1312 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1313 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1314 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1315 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1316 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1317 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1318 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1319 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1320 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1321 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1322 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1323 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1324 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1325 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1327 return ERROR_SUCCESS;
1330 /***********************************************************************
1331 * URLCache_HashKey (Internal)
1333 * Returns the hash key for a given string
1336 * hash key for the string
1339 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1341 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1342 * but the algorithm and result are not the same!
1344 static const unsigned char lookupTable[256] =
1346 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1347 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1348 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1349 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1350 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1351 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1352 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1353 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1354 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1355 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1356 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1357 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1358 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1359 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1360 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1361 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1362 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1363 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1364 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1365 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1366 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1367 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1368 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1369 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1370 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1371 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1372 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1373 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1374 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1375 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1376 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1377 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1382 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1383 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1385 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1387 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1388 key[i] = lookupTable[*lpszKey ^ key[i]];
1391 return *(DWORD *)key;
1394 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1396 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1399 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1401 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1402 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1403 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1406 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1408 /* structure of hash table:
1409 * 448 entries divided into 64 blocks
1410 * each block therefore contains a chain of 7 key/offset pairs
1411 * how position in table is calculated:
1412 * 1. the url is hashed in helper function
1413 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1414 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1417 * there can be multiple hash tables in the file and the offset to
1418 * the next one is stored in the header of the hash table
1420 DWORD key = URLCache_HashKey(lpszUrl);
1421 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1422 HASH_CACHEFILE_ENTRY * pHashEntry;
1423 DWORD dwHashTableNumber = 0;
1425 key >>= HASHTABLE_FLAG_BITS;
1427 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1428 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1429 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1432 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1434 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1437 /* make sure that it is in fact a hash entry */
1438 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1440 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1444 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1446 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1447 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1449 /* FIXME: we should make sure that this is the right element
1450 * before returning and claiming that it is. We can do this
1451 * by doing a simple compare between the URL we were given
1452 * and the URL stored in the entry. However, this assumes
1453 * we know the format of all the entries stored in the
1455 *ppHashEntry = pHashElement;
1463 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1468 urlA = heap_strdupWtoA(lpszUrl);
1471 SetLastError(ERROR_OUTOFMEMORY);
1475 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1480 /***********************************************************************
1481 * URLCache_HashEntrySetFlags (Internal)
1483 * Sets special bits in hash key
1489 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1491 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1494 /***********************************************************************
1495 * URLCache_DeleteEntryFromHash (Internal)
1497 * Searches all the hash tables in the index for the given URL and
1498 * then if found deletes the entry.
1501 * TRUE if the entry was found
1502 * FALSE if the entry could not be found
1505 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1507 pHashEntry->dwHashKey = HASHTABLE_DEL;
1511 /***********************************************************************
1512 * URLCache_AddEntryToHash (Internal)
1514 * Searches all the hash tables for a free slot based on the offset
1515 * generated from the hash key. If a free slot is found, the offset and
1516 * key are entered into the hash table.
1519 * ERROR_SUCCESS if the entry was added
1520 * Any other Win32 error code if the entry could not be added
1523 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1525 /* see URLCache_FindEntryInHash for structure of hash tables */
1527 DWORD key = URLCache_HashKey(lpszUrl);
1528 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1529 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1530 DWORD dwHashTableNumber = 0;
1533 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1535 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1536 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1537 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1540 pHashPrev = pHashEntry;
1542 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1544 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1547 /* make sure that it is in fact a hash entry */
1548 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1550 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1554 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1556 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1557 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1559 pHashElement->dwHashKey = key;
1560 pHashElement->dwOffsetEntry = dwOffsetEntry;
1561 return ERROR_SUCCESS;
1565 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1566 if (error != ERROR_SUCCESS)
1569 pHashEntry->HashTable[offset].dwHashKey = key;
1570 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1571 return ERROR_SUCCESS;
1574 /***********************************************************************
1575 * URLCache_CreateHashTable (Internal)
1577 * Creates a new hash table in free space and adds it to the chain of existing
1581 * ERROR_SUCCESS if the hash table was created
1582 * ERROR_DISK_FULL if the hash table could not be created
1585 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1587 DWORD dwOffset, error;
1590 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1593 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1596 pPrevHash->dwAddressNext = dwOffset;
1598 pHeader->dwOffsetFirstHashTable = dwOffset;
1599 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1600 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1601 (*ppHash)->dwAddressNext = 0;
1602 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1603 for (i = 0; i < HASHTABLE_SIZE; i++)
1605 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1606 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1608 return ERROR_SUCCESS;
1611 /***********************************************************************
1612 * URLCache_EnumHashTables (Internal)
1614 * Enumerates the hash tables in a container.
1617 * TRUE if an entry was found
1618 * FALSE if there are no more tables to enumerate.
1621 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1623 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1624 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1625 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1627 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1628 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1630 /* make sure that it is in fact a hash entry */
1631 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1633 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1634 (*pdwHashTableNumber)++;
1638 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1644 /***********************************************************************
1645 * URLCache_EnumHashTableEntries (Internal)
1647 * Enumerates entries in a hash table and returns the next non-free entry.
1650 * TRUE if an entry was found
1651 * FALSE if the hash table is empty or there are no more entries to
1655 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1656 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1658 for (; *index < HASHTABLE_SIZE ; (*index)++)
1660 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1663 *ppHashEntry = &pHashEntry->HashTable[*index];
1664 TRACE("entry found %d\n", *index);
1667 TRACE("no more entries (%d)\n", *index);
1671 /***********************************************************************
1672 * URLCache_DeleteCacheDirectory (Internal)
1674 * Erase a directory containing an URL cache.
1677 * TRUE success, FALSE failure/aborted.
1680 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1683 WCHAR path[MAX_PATH + 1];
1684 SHFILEOPSTRUCTW shfos;
1687 path_len = strlenW(lpszPath);
1688 if (path_len >= MAX_PATH)
1690 strcpyW(path, lpszPath);
1691 path[path_len + 1] = 0; /* double-NUL-terminate path */
1694 shfos.wFunc = FO_DELETE;
1697 shfos.fFlags = FOF_NOCONFIRMATION;
1698 shfos.fAnyOperationsAborted = FALSE;
1699 ret = SHFileOperationW(&shfos);
1701 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1702 return !(ret || shfos.fAnyOperationsAborted);
1705 /***********************************************************************
1706 * URLCache_IsLocked (Internal)
1708 * Checks if entry is locked. Unlocks it if possible.
1710 static BOOL URLCache_IsLocked(struct _HASH_ENTRY *hash_entry, URL_CACHEFILE_ENTRY *url_entry)
1713 ULARGE_INTEGER acc_time, time;
1715 if ((hash_entry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1718 GetSystemTimeAsFileTime(&cur_time);
1719 time.u.LowPart = cur_time.dwLowDateTime;
1720 time.u.HighPart = cur_time.dwHighDateTime;
1722 acc_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
1723 acc_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
1725 time.QuadPart -= acc_time.QuadPart;
1727 /* check if entry was locked for at least a day */
1728 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1729 URLCache_HashEntrySetFlags(hash_entry, HASHTABLE_URL);
1730 url_entry->dwUseCount = 0;
1737 /***********************************************************************
1738 * GetUrlCacheEntryInfoExA (WININET.@)
1741 BOOL WINAPI GetUrlCacheEntryInfoExA(
1743 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1744 LPDWORD lpdwCacheEntryInfoBufSize,
1746 LPDWORD lpdwReserved,
1750 LPURLCACHE_HEADER pHeader;
1751 struct _HASH_ENTRY * pHashEntry;
1752 const CACHEFILE_ENTRY * pEntry;
1753 const URL_CACHEFILE_ENTRY * pUrlEntry;
1754 URLCACHECONTAINER * pContainer;
1757 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1758 debugstr_a(lpszUrl),
1760 lpdwCacheEntryInfoBufSize,
1766 if ((lpszReserved != NULL) ||
1767 (lpdwReserved != NULL) ||
1768 (lpReserved != NULL))
1770 ERR("Reserved value was not 0\n");
1771 SetLastError(ERROR_INVALID_PARAMETER);
1774 if (dwFlags & ~GET_INSTALLED_ENTRY)
1775 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1777 error = URLCacheContainers_FindContainerA(lpszUrl, &pContainer);
1778 if (error != ERROR_SUCCESS)
1780 SetLastError(error);
1784 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1785 if (error != ERROR_SUCCESS)
1787 SetLastError(error);
1791 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1794 if (!URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1796 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1797 WARN("entry %s not found!\n", debugstr_a(lpszUrl));
1798 SetLastError(ERROR_FILE_NOT_FOUND);
1802 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1803 if (pEntry->dwSignature != URL_SIGNATURE)
1805 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1806 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1807 SetLastError(ERROR_FILE_NOT_FOUND);
1811 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1812 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1813 if (pUrlEntry->dwOffsetHeaderInfo)
1814 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1816 if((dwFlags & GET_INSTALLED_ENTRY) && !(pUrlEntry->CacheEntryType & INSTALLED_CACHE_ENTRY))
1818 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1819 SetLastError(ERROR_FILE_NOT_FOUND);
1823 if (lpdwCacheEntryInfoBufSize)
1825 if (!lpCacheEntryInfo)
1826 *lpdwCacheEntryInfoBufSize = 0;
1828 error = URLCache_CopyEntry(
1832 lpdwCacheEntryInfoBufSize,
1835 if (error != ERROR_SUCCESS)
1837 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1838 SetLastError(error);
1841 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1844 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1849 /***********************************************************************
1850 * GetUrlCacheEntryInfoA (WININET.@)
1853 BOOL WINAPI GetUrlCacheEntryInfoA(
1854 IN LPCSTR lpszUrlName,
1855 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1856 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1859 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1860 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1863 /***********************************************************************
1864 * GetUrlCacheEntryInfoW (WININET.@)
1867 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1868 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1869 LPDWORD lpdwCacheEntryInfoBufferSize)
1871 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1872 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1875 /***********************************************************************
1876 * GetUrlCacheEntryInfoExW (WININET.@)
1879 BOOL WINAPI GetUrlCacheEntryInfoExW(
1881 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1882 LPDWORD lpdwCacheEntryInfoBufSize,
1883 LPWSTR lpszReserved,
1884 LPDWORD lpdwReserved,
1888 LPURLCACHE_HEADER pHeader;
1889 struct _HASH_ENTRY * pHashEntry;
1890 const CACHEFILE_ENTRY * pEntry;
1891 const URL_CACHEFILE_ENTRY * pUrlEntry;
1892 URLCACHECONTAINER * pContainer;
1895 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1896 debugstr_w(lpszUrl),
1898 lpdwCacheEntryInfoBufSize,
1904 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1905 dwFlags &= ~GET_INSTALLED_ENTRY;
1907 if ((lpszReserved != NULL) ||
1908 (lpdwReserved != NULL) ||
1909 (lpReserved != NULL))
1911 ERR("Reserved value was not 0\n");
1912 SetLastError(ERROR_INVALID_PARAMETER);
1916 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1918 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1919 if (error != ERROR_SUCCESS)
1921 SetLastError(error);
1925 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1926 if (error != ERROR_SUCCESS)
1928 SetLastError(error);
1932 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1935 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1937 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1938 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1939 SetLastError(ERROR_FILE_NOT_FOUND);
1943 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1944 if (pEntry->dwSignature != URL_SIGNATURE)
1946 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1947 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1948 SetLastError(ERROR_FILE_NOT_FOUND);
1952 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1953 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1954 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1956 if (lpdwCacheEntryInfoBufSize)
1958 if (!lpCacheEntryInfo)
1959 *lpdwCacheEntryInfoBufSize = 0;
1961 error = URLCache_CopyEntry(
1964 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1965 lpdwCacheEntryInfoBufSize,
1967 TRUE /* UNICODE */);
1968 if (error != ERROR_SUCCESS)
1970 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1971 SetLastError(error);
1974 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1977 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1982 /***********************************************************************
1983 * SetUrlCacheEntryInfoA (WININET.@)
1985 BOOL WINAPI SetUrlCacheEntryInfoA(
1987 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1988 DWORD dwFieldControl)
1990 LPURLCACHE_HEADER pHeader;
1991 struct _HASH_ENTRY * pHashEntry;
1992 CACHEFILE_ENTRY * pEntry;
1993 URLCACHECONTAINER * pContainer;
1996 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1998 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1999 if (error != ERROR_SUCCESS)
2001 SetLastError(error);
2005 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2006 if (error != ERROR_SUCCESS)
2008 SetLastError(error);
2012 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2015 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2017 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2018 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2019 SetLastError(ERROR_FILE_NOT_FOUND);
2023 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2024 if (pEntry->dwSignature != URL_SIGNATURE)
2026 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2027 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2028 SetLastError(ERROR_FILE_NOT_FOUND);
2032 URLCache_SetEntryInfo(
2033 (URL_CACHEFILE_ENTRY *)pEntry,
2034 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
2037 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2042 /***********************************************************************
2043 * SetUrlCacheEntryInfoW (WININET.@)
2045 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
2047 LPURLCACHE_HEADER pHeader;
2048 struct _HASH_ENTRY * pHashEntry;
2049 CACHEFILE_ENTRY * pEntry;
2050 URLCACHECONTAINER * pContainer;
2053 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
2055 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
2056 if (error != ERROR_SUCCESS)
2058 SetLastError(error);
2062 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2063 if (error != ERROR_SUCCESS)
2065 SetLastError(error);
2069 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2072 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
2074 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2075 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
2076 SetLastError(ERROR_FILE_NOT_FOUND);
2080 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2081 if (pEntry->dwSignature != URL_SIGNATURE)
2083 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2084 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2085 SetLastError(ERROR_FILE_NOT_FOUND);
2089 URLCache_SetEntryInfo(
2090 (URL_CACHEFILE_ENTRY *)pEntry,
2094 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2099 /***********************************************************************
2100 * RetrieveUrlCacheEntryFileA (WININET.@)
2103 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2104 IN LPCSTR lpszUrlName,
2105 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2106 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2110 LPURLCACHE_HEADER pHeader;
2111 struct _HASH_ENTRY * pHashEntry;
2112 CACHEFILE_ENTRY * pEntry;
2113 URL_CACHEFILE_ENTRY * pUrlEntry;
2114 URLCACHECONTAINER * pContainer;
2117 TRACE("(%s, %p, %p, 0x%08x)\n",
2118 debugstr_a(lpszUrlName),
2120 lpdwCacheEntryInfoBufferSize,
2123 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2124 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2126 SetLastError(ERROR_INVALID_PARAMETER);
2130 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2131 if (error != ERROR_SUCCESS)
2133 SetLastError(error);
2137 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2138 if (error != ERROR_SUCCESS)
2140 SetLastError(error);
2144 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2147 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2149 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2150 TRACE("entry %s not found!\n", lpszUrlName);
2151 SetLastError(ERROR_FILE_NOT_FOUND);
2155 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2156 if (pEntry->dwSignature != URL_SIGNATURE)
2158 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2159 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2160 SetLastError(ERROR_FILE_NOT_FOUND);
2164 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2165 if (!pUrlEntry->dwOffsetLocalName)
2167 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2168 SetLastError(ERROR_INVALID_DATA);
2172 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2173 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2175 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2176 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2178 if (error != ERROR_SUCCESS)
2180 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2181 SetLastError(error);
2184 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2186 pUrlEntry->dwHitRate++;
2187 pUrlEntry->dwUseCount++;
2188 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2189 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2191 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2196 /***********************************************************************
2197 * RetrieveUrlCacheEntryFileW (WININET.@)
2200 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2201 IN LPCWSTR lpszUrlName,
2202 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2203 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2207 LPURLCACHE_HEADER pHeader;
2208 struct _HASH_ENTRY * pHashEntry;
2209 CACHEFILE_ENTRY * pEntry;
2210 URL_CACHEFILE_ENTRY * pUrlEntry;
2211 URLCACHECONTAINER * pContainer;
2214 TRACE("(%s, %p, %p, 0x%08x)\n",
2215 debugstr_w(lpszUrlName),
2217 lpdwCacheEntryInfoBufferSize,
2220 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2221 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2223 SetLastError(ERROR_INVALID_PARAMETER);
2227 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2228 if (error != ERROR_SUCCESS)
2230 SetLastError(error);
2234 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2235 if (error != ERROR_SUCCESS)
2237 SetLastError(error);
2241 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2244 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2246 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2247 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2248 SetLastError(ERROR_FILE_NOT_FOUND);
2252 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2253 if (pEntry->dwSignature != URL_SIGNATURE)
2255 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2256 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2257 SetLastError(ERROR_FILE_NOT_FOUND);
2261 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2262 if (!pUrlEntry->dwOffsetLocalName)
2264 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2265 SetLastError(ERROR_INVALID_DATA);
2269 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2270 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2272 error = URLCache_CopyEntry(
2275 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2276 lpdwCacheEntryInfoBufferSize,
2278 TRUE /* UNICODE */);
2279 if (error != ERROR_SUCCESS)
2281 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2282 SetLastError(error);
2285 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2287 pUrlEntry->dwHitRate++;
2288 pUrlEntry->dwUseCount++;
2289 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2290 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2292 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2297 static BOOL DeleteUrlCacheEntryInternal(const URLCACHECONTAINER * pContainer,
2298 LPURLCACHE_HEADER pHeader, struct _HASH_ENTRY *pHashEntry)
2300 CACHEFILE_ENTRY * pEntry;
2301 URL_CACHEFILE_ENTRY * pUrlEntry;
2303 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2304 if (pEntry->dwSignature != URL_SIGNATURE)
2306 FIXME("Trying to delete entry of unknown format %s\n",
2307 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2308 SetLastError(ERROR_FILE_NOT_FOUND);
2312 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2313 if(URLCache_IsLocked(pHashEntry, pUrlEntry))
2315 TRACE("Trying to delete locked entry\n");
2316 pUrlEntry->CacheEntryType |= PENDING_DELETE_CACHE_ENTRY;
2317 SetLastError(ERROR_SHARING_VIOLATION);
2321 if(!URLCache_DeleteFile(pContainer, pHeader, pUrlEntry))
2323 URLCache_DeleteEntry(pHeader, pEntry);
2327 /* Add entry to leaked files list */
2328 pUrlEntry->CacheFileEntry.dwSignature = LEAK_SIGNATURE;
2329 pUrlEntry->dwExemptDelta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2330 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->dwOffsetEntry;
2333 URLCache_DeleteEntryFromHash(pHashEntry);
2337 static HANDLE free_cache_running;
2338 static HANDLE dll_unload_event;
2339 static DWORD WINAPI handle_full_cache_worker(void *param)
2341 FreeUrlCacheSpaceW(NULL, 20, 0);
2342 ReleaseSemaphore(free_cache_running, 1, NULL);
2346 static void handle_full_cache(void)
2348 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2349 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2350 ReleaseSemaphore(free_cache_running, 1, NULL);
2354 /* Enumerates entries in cache, allows cache unlocking between calls. */
2355 static BOOL urlcache_next_entry(URLCACHE_HEADER *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2356 struct _HASH_ENTRY **hash_entry, CACHEFILE_ENTRY **entry)
2358 HASH_CACHEFILE_ENTRY *hashtable_entry;
2363 if(!*hash_table_off) {
2364 *hash_table_off = header->dwOffsetFirstHashTable;
2365 *hash_table_entry = 0;
2367 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2369 if(*hash_table_off >= header->dwFileSize) {
2370 *hash_table_off = 0;
2374 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2377 if(hashtable_entry->CacheFileEntry.dwSignature != HASH_SIGNATURE) {
2378 *hash_table_off = 0;
2383 if(*hash_table_entry >= HASHTABLE_SIZE) {
2384 *hash_table_off = hashtable_entry->dwAddressNext;
2385 if(!*hash_table_off) {
2386 *hash_table_off = 0;
2390 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2391 *hash_table_entry = 0;
2394 if(hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_DEL &&
2395 hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_FREE) {
2396 *hash_entry = &hashtable_entry->HashTable[*hash_table_entry];
2397 *entry = (CACHEFILE_ENTRY*)((LPBYTE)header + hashtable_entry->HashTable[*hash_table_entry].dwOffsetEntry);
2398 (*hash_table_entry)++;
2402 (*hash_table_entry)++;
2405 *hash_table_off = 0;
2409 /* Rates an urlcache entry to determine if it can be deleted.
2411 * Score 0 means that entry can safely be removed, the bigger rating
2412 * the smaller chance of entry being removed.
2413 * DWORD_MAX means that entry can't be deleted at all.
2415 * Rating system is currently not fully compatible with native implementation.
2417 static DWORD urlcache_rate_entry(URL_CACHEFILE_ENTRY *url_entry, FILETIME *cur_time)
2419 ULARGE_INTEGER time, access_time;
2422 access_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
2423 access_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
2425 time.u.LowPart = cur_time->dwLowDateTime;
2426 time.u.HighPart = cur_time->dwHighDateTime;
2428 /* Don't touch entries that were added less than 10 minutes ago */
2429 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2432 if(url_entry->CacheEntryType & STICKY_CACHE_ENTRY)
2433 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->dwExemptDelta*FILETIME_SECOND)
2436 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2437 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2439 if(url_entry->dwHitRate > 100)
2442 rating += url_entry->dwHitRate;
2447 static int dword_cmp(const void *p1, const void *p2)
2449 return *(const DWORD*)p1 - *(const DWORD*)p2;
2452 /***********************************************************************
2453 * FreeUrlCacheSpaceW (WININET.@)
2455 * Frees up some cache.
2458 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2459 * size [I] Percentage of the cache that should be free.
2460 * filter [I] Which entries can't be deleted (CacheEntryType)
2463 * TRUE success. FALSE failure.
2466 * This implementation just retrieves the path of the cache directory, and
2467 * deletes its contents from the filesystem. The correct approach would
2468 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2470 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2472 URLCACHECONTAINER *container;
2473 DWORD path_len, err;
2475 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2477 if(size<1 || size>100) {
2478 SetLastError(ERROR_INVALID_PARAMETER);
2483 path_len = strlenW(cache_path);
2484 if(cache_path[path_len-1] == '\\')
2490 if(size==100 && !filter) {
2491 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2493 /* When cache_path==NULL only clean Temporary Internet Files */
2494 if((!path_len && container->cache_prefix[0]==0) ||
2495 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2496 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2500 WaitForSingleObject(container->hMutex, INFINITE);
2502 /* unlock, delete, recreate and lock cache */
2503 URLCacheContainer_CloseIndex(container);
2504 ret_del = URLCache_DeleteCacheDirectory(container->path);
2505 err = URLCacheContainer_OpenIndex(container, MIN_BLOCK_NO);
2507 ReleaseMutex(container->hMutex);
2508 if(!ret_del || (err != ERROR_SUCCESS))
2516 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2518 URLCACHE_HEADER *header;
2519 struct _HASH_ENTRY *hash_entry;
2520 CACHEFILE_ENTRY *entry;
2521 URL_CACHEFILE_ENTRY *url_entry;
2522 ULONGLONG desired_size, cur_size;
2523 DWORD delete_factor, hash_table_off, hash_table_entry;
2524 DWORD rate[100], rate_no;
2527 if((path_len || container->cache_prefix[0]!=0) &&
2528 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2529 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2532 err = URLCacheContainer_OpenIndex(container, MIN_BLOCK_NO);
2533 if(err != ERROR_SUCCESS)
2536 header = URLCacheContainer_LockIndex(container);
2540 urlcache_clean_leaked_entries(container, header);
2542 desired_size = header->CacheLimit.QuadPart*(100-size)/100;
2543 cur_size = header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart;
2544 if(cur_size <= desired_size)
2547 delete_factor = (cur_size-desired_size)*100/cur_size;
2549 if(!delete_factor) {
2550 URLCacheContainer_UnlockIndex(container, header);
2555 hash_table_entry = 0;
2557 GetSystemTimeAsFileTime(&cur_time);
2558 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2559 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2560 if(entry->dwSignature != URL_SIGNATURE) {
2561 WARN("only url entries are currently supported\n");
2565 url_entry = (URL_CACHEFILE_ENTRY*)entry;
2566 if(url_entry->CacheEntryType & filter)
2569 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2570 if(rate[rate_no] != -1)
2575 TRACE("nothing to delete\n");
2576 URLCacheContainer_UnlockIndex(container, header);
2580 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2582 delete_factor = delete_factor*rate_no/100;
2583 delete_factor = rate[delete_factor];
2584 TRACE("deleting files with rating %d or less\n", delete_factor);
2587 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2588 if(entry->dwSignature != URL_SIGNATURE)
2591 url_entry = (URL_CACHEFILE_ENTRY*)entry;
2592 if(url_entry->CacheEntryType & filter)
2595 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2596 TRACE("deleting file: %s\n", (char*)url_entry+url_entry->dwOffsetLocalName);
2597 DeleteUrlCacheEntryInternal(container, header, hash_entry);
2599 if(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart <= desired_size)
2602 /* Allow other threads to use cache while cleaning */
2603 URLCacheContainer_UnlockIndex(container, header);
2604 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2605 TRACE("got dll_unload_event - finishing\n");
2609 header = URLCacheContainer_LockIndex(container);
2613 TRACE("cache size after cleaning 0x%s/0x%s\n",
2614 wine_dbgstr_longlong(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart),
2615 wine_dbgstr_longlong(header->CacheLimit.QuadPart));
2616 URLCacheContainer_UnlockIndex(container, header);
2622 /***********************************************************************
2623 * FreeUrlCacheSpaceA (WININET.@)
2625 * See FreeUrlCacheSpaceW.
2627 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2630 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2631 if (lpszCachePath == NULL || path != NULL)
2632 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2637 /***********************************************************************
2638 * UnlockUrlCacheEntryFileA (WININET.@)
2641 BOOL WINAPI UnlockUrlCacheEntryFileA(
2642 IN LPCSTR lpszUrlName,
2646 LPURLCACHE_HEADER pHeader;
2647 struct _HASH_ENTRY * pHashEntry;
2648 CACHEFILE_ENTRY * pEntry;
2649 URL_CACHEFILE_ENTRY * pUrlEntry;
2650 URLCACHECONTAINER * pContainer;
2653 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2657 ERR("dwReserved != 0\n");
2658 SetLastError(ERROR_INVALID_PARAMETER);
2662 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2663 if (error != ERROR_SUCCESS)
2665 SetLastError(error);
2669 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2670 if (error != ERROR_SUCCESS)
2672 SetLastError(error);
2676 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2679 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2681 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2682 TRACE("entry %s not found!\n", lpszUrlName);
2683 SetLastError(ERROR_FILE_NOT_FOUND);
2687 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2688 if (pEntry->dwSignature != URL_SIGNATURE)
2690 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2691 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2692 SetLastError(ERROR_FILE_NOT_FOUND);
2696 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2698 if (pUrlEntry->dwUseCount == 0)
2700 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2703 pUrlEntry->dwUseCount--;
2704 if (!pUrlEntry->dwUseCount)
2706 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2707 if (pUrlEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)
2708 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2711 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2716 /***********************************************************************
2717 * UnlockUrlCacheEntryFileW (WININET.@)
2720 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2722 LPURLCACHE_HEADER pHeader;
2723 struct _HASH_ENTRY * pHashEntry;
2724 CACHEFILE_ENTRY * pEntry;
2725 URL_CACHEFILE_ENTRY * pUrlEntry;
2726 URLCACHECONTAINER * pContainer;
2729 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2733 ERR("dwReserved != 0\n");
2734 SetLastError(ERROR_INVALID_PARAMETER);
2738 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2739 if (error != ERROR_SUCCESS)
2741 SetLastError(error);
2745 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2746 if (error != ERROR_SUCCESS)
2748 SetLastError(error);
2752 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2755 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2757 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2758 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2759 SetLastError(ERROR_FILE_NOT_FOUND);
2763 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2764 if (pEntry->dwSignature != URL_SIGNATURE)
2766 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2767 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2768 SetLastError(ERROR_FILE_NOT_FOUND);
2772 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2774 if (pUrlEntry->dwUseCount == 0)
2776 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2779 pUrlEntry->dwUseCount--;
2780 if (!pUrlEntry->dwUseCount)
2781 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2783 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2788 /***********************************************************************
2789 * CreateUrlCacheEntryA (WININET.@)
2792 BOOL WINAPI CreateUrlCacheEntryA(
2793 IN LPCSTR lpszUrlName,
2794 IN DWORD dwExpectedFileSize,
2795 IN LPCSTR lpszFileExtension,
2796 OUT LPSTR lpszFileName,
2801 WCHAR *file_extension = NULL;
2802 WCHAR file_name[MAX_PATH];
2803 BOOL bSuccess = FALSE;
2806 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2807 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2809 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2811 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2813 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2815 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2821 dwError = GetLastError();
2826 dwError = GetLastError();
2828 heap_free(file_extension);
2832 dwError = GetLastError();
2834 heap_free(url_name);
2835 if (!bSuccess) SetLastError(dwError);
2839 /***********************************************************************
2840 * CreateUrlCacheEntryW (WININET.@)
2843 BOOL WINAPI CreateUrlCacheEntryW(
2844 IN LPCWSTR lpszUrlName,
2845 IN DWORD dwExpectedFileSize,
2846 IN LPCWSTR lpszFileExtension,
2847 OUT LPWSTR lpszFileName,
2851 URLCACHECONTAINER * pContainer;
2852 LPURLCACHE_HEADER pHeader;
2853 CHAR szFile[MAX_PATH];
2854 WCHAR szExtension[MAX_PATH];
2855 LPCWSTR lpszUrlPart;
2857 LPCWSTR lpszFileNameExtension;
2858 LPWSTR lpszFileNameNoPath;
2860 int countnoextension;
2863 BOOL bFound = FALSE;
2864 BOOL generate_name = FALSE;
2870 static const WCHAR szWWW[] = {'w','w','w',0};
2872 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2873 debugstr_w(lpszUrlName),
2875 debugstr_w(lpszFileExtension),
2880 FIXME("dwReserved 0x%08x\n", dwReserved);
2882 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2884 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2887 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2889 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2891 lpszUrlEnd = lpszUrlPart;
2893 for (lpszUrlPart = lpszUrlEnd;
2894 (lpszUrlPart >= lpszUrlName);
2897 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2907 if (!lstrcmpW(lpszUrlPart, szWWW))
2909 lpszUrlPart += lstrlenW(szWWW);
2912 count = lpszUrlEnd - lpszUrlPart;
2914 if (bFound && (count < MAX_PATH))
2916 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2920 while(len && szFile[--len] == '/') szFile[len] = '\0';
2922 /* FIXME: get rid of illegal characters like \, / and : */
2923 TRACE("File name: %s\n", debugstr_a(szFile));
2927 generate_name = TRUE;
2931 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2932 if (error != ERROR_SUCCESS)
2934 SetLastError(error);
2938 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2939 if (error != ERROR_SUCCESS)
2941 SetLastError(error);
2945 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2948 if(pHeader->DirectoryCount)
2949 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2951 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2953 lBufferSize = MAX_PATH * sizeof(WCHAR);
2954 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2956 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2957 debugstr_a(szFile), lBufferSize);
2958 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2962 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2964 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2965 lpszFileNameNoPath >= lpszFileName;
2966 --lpszFileNameNoPath)
2968 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2972 countnoextension = lstrlenW(lpszFileNameNoPath);
2973 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2974 if (lpszFileNameExtension)
2975 countnoextension -= lstrlenW(lpszFileNameExtension);
2976 *szExtension = '\0';
2978 if (lpszFileExtension)
2980 szExtension[0] = '.';
2981 lstrcpyW(szExtension+1, lpszFileExtension);
2984 for (i = 0; i<255 && !generate_name; i++)
2986 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2989 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2990 for (p = lpszFileNameNoPath + 1; *p; p++)
2996 case '/': case '\\':
3003 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3005 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3006 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3007 if (hFile != INVALID_HANDLE_VALUE)
3014 /* Try to generate random name */
3015 GetSystemTimeAsFileTime(&ft);
3016 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
3018 for(i=0; i<255; i++)
3021 ULONGLONG n = ft.dwHighDateTime;
3023 n += ft.dwLowDateTime;
3024 n ^= (ULONGLONG)i<<48;
3030 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
3033 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3034 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3035 if (hFile != INVALID_HANDLE_VALUE)
3042 WARN("Could not find a unique filename\n");
3046 /***********************************************************************
3047 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3049 * The bug we are compensating for is that some drongo at Microsoft
3050 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3051 * As a consequence, CommitUrlCacheEntryA has been effectively
3052 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3053 * is still defined as LPCWSTR. The result (other than madness) is
3054 * that we always need to store lpHeaderInfo in CP_ACP rather than
3055 * in UTF16, and we need to avoid converting lpHeaderInfo in
3056 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3057 * result will lose data for arbitrary binary data.
3060 static BOOL CommitUrlCacheEntryInternal(
3061 IN LPCWSTR lpszUrlName,
3062 IN LPCWSTR lpszLocalFileName,
3063 IN FILETIME ExpireTime,
3064 IN FILETIME LastModifiedTime,
3065 IN DWORD CacheEntryType,
3066 IN LPBYTE lpHeaderInfo,
3067 IN DWORD dwHeaderSize,
3068 IN LPCWSTR lpszFileExtension,
3069 IN LPCWSTR lpszOriginalUrl
3072 URLCACHECONTAINER * pContainer;
3073 LPURLCACHE_HEADER pHeader;
3074 struct _HASH_ENTRY * pHashEntry;
3075 CACHEFILE_ENTRY * pEntry;
3076 URL_CACHEFILE_ENTRY * pUrlEntry;
3077 DWORD url_entry_offset;
3078 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3079 DWORD dwOffsetLocalFileName = 0;
3080 DWORD dwOffsetHeader = 0;
3081 DWORD dwOffsetFileExtension = 0;
3082 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3083 LARGE_INTEGER file_size;
3085 char achFile[MAX_PATH];
3086 LPSTR lpszUrlNameA = NULL;
3087 LPSTR lpszFileExtensionA = NULL;
3088 char *pchLocalFileName = 0;
3090 DWORD exempt_delta = 0;
3093 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3094 debugstr_w(lpszUrlName),
3095 debugstr_w(lpszLocalFileName),
3099 debugstr_w(lpszFileExtension),
3100 debugstr_w(lpszOriginalUrl));
3102 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3104 SetLastError(ERROR_INVALID_PARAMETER);
3107 if (lpszOriginalUrl)
3108 WARN(": lpszOriginalUrl ignored\n");
3110 memset(&file_attr, 0, sizeof(file_attr));
3111 if (lpszLocalFileName)
3113 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3116 file_size.u.LowPart = file_attr.nFileSizeLow;
3117 file_size.u.HighPart = file_attr.nFileSizeHigh;
3119 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3120 if (error != ERROR_SUCCESS)
3122 SetLastError(error);
3126 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3127 if (error != ERROR_SUCCESS)
3129 SetLastError(error);
3133 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3136 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3139 error = GetLastError();
3143 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3145 error = GetLastError();
3149 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3151 URL_CACHEFILE_ENTRY *pUrlEntry = (URL_CACHEFILE_ENTRY*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3152 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3154 TRACE("Trying to overwrite locked entry\n");
3155 error = ERROR_SHARING_VIOLATION;
3159 hit_rate = pUrlEntry->dwHitRate;
3160 exempt_delta = pUrlEntry->dwExemptDelta;
3161 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3164 if (pHeader->DirectoryCount)
3167 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3169 if (lpszLocalFileName)
3171 BOOL bFound = FALSE;
3173 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3175 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3176 error = ERROR_INVALID_PARAMETER;
3180 /* skip container path prefix */
3181 lpszLocalFileName += lstrlenW(pContainer->path);
3183 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3184 pchLocalFileName = achFile;
3186 if(pHeader->DirectoryCount)
3188 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
3190 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
3199 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3200 error = ERROR_INVALID_PARAMETER;
3204 lpszLocalFileName += DIR_LENGTH + 1;
3205 pchLocalFileName += DIR_LENGTH + 1;
3209 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3210 if (lpszLocalFileName)
3212 dwOffsetLocalFileName = dwBytesNeeded;
3213 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3217 dwOffsetHeader = dwBytesNeeded;
3218 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3220 if (lpszFileExtensionA)
3222 dwOffsetFileExtension = dwBytesNeeded;
3223 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3226 /* round up to next block */
3227 if (dwBytesNeeded % BLOCKSIZE)
3229 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3230 dwBytesNeeded += BLOCKSIZE;
3233 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3234 while (error == ERROR_HANDLE_DISK_FULL)
3236 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3237 if (error == ERROR_SUCCESS)
3238 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3240 if (error != ERROR_SUCCESS)
3243 /* FindFirstFreeEntry fills in blocks used */
3244 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3245 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3246 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
3247 pUrlEntry->CacheDir = cDirectory;
3248 pUrlEntry->CacheEntryType = CacheEntryType | pContainer->default_entry_type;
3249 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
3250 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3252 /* Sticky entries have a default exempt time of one day */
3253 exempt_delta = 86400;
3255 pUrlEntry->dwExemptDelta = exempt_delta;
3256 pUrlEntry->dwHitRate = hit_rate+1;
3257 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
3258 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
3259 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
3260 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
3261 pUrlEntry->size.QuadPart = file_size.QuadPart;
3262 pUrlEntry->dwUseCount = 0;
3263 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
3264 pUrlEntry->LastModifiedTime = LastModifiedTime;
3265 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
3266 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
3267 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->LastWriteDate, &pUrlEntry->LastWriteTime);
3270 pUrlEntry->dwUnknown1 = 0;
3271 pUrlEntry->dwUnknown2 = 0;
3272 pUrlEntry->dwUnknown3 = 0x60;
3273 pUrlEntry->Unknown4 = 0;
3274 pUrlEntry->wUnknown5 = 0x1010;
3275 pUrlEntry->dwUnknown7 = 0;
3276 pUrlEntry->dwUnknown8 = 0;
3279 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
3280 if (dwOffsetLocalFileName)
3281 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3283 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3284 if (dwOffsetFileExtension)
3285 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3287 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3288 while (error == ERROR_HANDLE_DISK_FULL)
3290 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3291 if (error == ERROR_SUCCESS)
3293 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
3294 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3295 url_entry_offset, HASHTABLE_URL);
3298 if (error != ERROR_SUCCESS)
3299 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
3302 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3303 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
3304 if (CacheEntryType & STICKY_CACHE_ENTRY)
3305 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
3307 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
3308 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
3309 pHeader->CacheLimit.QuadPart)
3310 handle_full_cache();
3314 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3315 heap_free(lpszUrlNameA);
3316 heap_free(lpszFileExtensionA);
3318 if (error == ERROR_SUCCESS)
3322 SetLastError(error);
3327 /***********************************************************************
3328 * CommitUrlCacheEntryA (WININET.@)
3331 BOOL WINAPI CommitUrlCacheEntryA(
3332 IN LPCSTR lpszUrlName,
3333 IN LPCSTR lpszLocalFileName,
3334 IN FILETIME ExpireTime,
3335 IN FILETIME LastModifiedTime,
3336 IN DWORD CacheEntryType,
3337 IN LPBYTE lpHeaderInfo,
3338 IN DWORD dwHeaderSize,
3339 IN LPCSTR lpszFileExtension,
3340 IN LPCSTR lpszOriginalUrl
3343 WCHAR *url_name = NULL;
3344 WCHAR *local_file_name = NULL;
3345 WCHAR *original_url = NULL;
3346 WCHAR *file_extension = NULL;
3347 BOOL bSuccess = FALSE;
3349 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3350 debugstr_a(lpszUrlName),
3351 debugstr_a(lpszLocalFileName),
3355 debugstr_a(lpszFileExtension),
3356 debugstr_a(lpszOriginalUrl));
3358 url_name = heap_strdupAtoW(lpszUrlName);
3362 if (lpszLocalFileName)
3364 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3365 if (!local_file_name)
3368 if (lpszFileExtension)
3370 file_extension = heap_strdupAtoW(lpszFileExtension);
3371 if (!file_extension)
3374 if (lpszOriginalUrl)
3376 original_url = heap_strdupAtoW(lpszOriginalUrl);
3381 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3382 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3383 file_extension, original_url);
3386 heap_free(original_url);
3387 heap_free(file_extension);
3388 heap_free(local_file_name);
3389 heap_free(url_name);
3393 /***********************************************************************
3394 * CommitUrlCacheEntryW (WININET.@)
3397 BOOL WINAPI CommitUrlCacheEntryW(
3398 IN LPCWSTR lpszUrlName,
3399 IN LPCWSTR lpszLocalFileName,
3400 IN FILETIME ExpireTime,
3401 IN FILETIME LastModifiedTime,
3402 IN DWORD CacheEntryType,
3403 IN LPWSTR lpHeaderInfo,
3404 IN DWORD dwHeaderSize,
3405 IN LPCWSTR lpszFileExtension,
3406 IN LPCWSTR lpszOriginalUrl
3410 BOOL bSuccess = FALSE;
3412 CHAR *header_info = NULL;
3414 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3415 debugstr_w(lpszUrlName),
3416 debugstr_w(lpszLocalFileName),
3420 debugstr_w(lpszFileExtension),
3421 debugstr_w(lpszOriginalUrl));
3423 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3426 len = strlen(header_info);
3427 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3428 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3434 dwError = GetLastError();
3438 heap_free(header_info);
3440 SetLastError(dwError);
3446 /***********************************************************************
3447 * ReadUrlCacheEntryStream (WININET.@)
3450 BOOL WINAPI ReadUrlCacheEntryStream(
3451 IN HANDLE hUrlCacheStream,
3452 IN DWORD dwLocation,
3453 IN OUT LPVOID lpBuffer,
3454 IN OUT LPDWORD lpdwLen,
3458 /* Get handle to file from 'stream' */
3459 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3461 if (dwReserved != 0)
3463 ERR("dwReserved != 0\n");
3464 SetLastError(ERROR_INVALID_PARAMETER);
3468 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3470 SetLastError(ERROR_INVALID_HANDLE);
3474 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3476 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3479 /***********************************************************************
3480 * RetrieveUrlCacheEntryStreamA (WININET.@)
3483 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3484 IN LPCSTR lpszUrlName,
3485 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3486 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3487 IN BOOL fRandomRead,
3491 /* NOTE: this is not the same as the way that the native
3492 * version allocates 'stream' handles. I did it this way
3493 * as it is much easier and no applications should depend
3494 * on this behaviour. (Native version appears to allocate
3495 * indices into a table)
3497 STREAM_HANDLE * pStream;
3500 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3501 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3503 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3505 lpdwCacheEntryInfoBufferSize,
3511 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3516 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3518 if (hFile == INVALID_HANDLE_VALUE)
3521 /* allocate handle storage space */
3522 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3526 SetLastError(ERROR_OUTOFMEMORY);
3530 pStream->hFile = hFile;
3531 strcpy(pStream->lpszUrl, lpszUrlName);
3535 /***********************************************************************
3536 * RetrieveUrlCacheEntryStreamW (WININET.@)
3539 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3540 IN LPCWSTR lpszUrlName,
3541 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3542 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3543 IN BOOL fRandomRead,
3549 /* NOTE: this is not the same as the way that the native
3550 * version allocates 'stream' handles. I did it this way
3551 * as it is much easier and no applications should depend
3552 * on this behaviour. (Native version appears to allocate
3553 * indices into a table)
3555 STREAM_HANDLE * pStream;
3558 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3559 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3561 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3563 lpdwCacheEntryInfoBufferSize,
3569 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3574 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3576 if (hFile == INVALID_HANDLE_VALUE)
3579 /* allocate handle storage space */
3580 size = sizeof(STREAM_HANDLE);
3581 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3583 pStream = heap_alloc(size);
3587 SetLastError(ERROR_OUTOFMEMORY);
3591 pStream->hFile = hFile;
3592 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3596 /***********************************************************************
3597 * UnlockUrlCacheEntryStream (WININET.@)
3600 BOOL WINAPI UnlockUrlCacheEntryStream(
3601 IN HANDLE hUrlCacheStream,
3605 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3607 if (dwReserved != 0)
3609 ERR("dwReserved != 0\n");
3610 SetLastError(ERROR_INVALID_PARAMETER);
3614 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3616 SetLastError(ERROR_INVALID_HANDLE);
3620 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3623 CloseHandle(pStream->hFile);
3629 /***********************************************************************
3630 * DeleteUrlCacheEntryA (WININET.@)
3633 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3635 URLCACHECONTAINER * pContainer;
3636 LPURLCACHE_HEADER pHeader;
3637 struct _HASH_ENTRY * pHashEntry;
3641 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3643 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3644 if (error != ERROR_SUCCESS)
3646 SetLastError(error);
3650 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3651 if (error != ERROR_SUCCESS)
3653 SetLastError(error);
3657 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3660 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3662 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3663 TRACE("entry %s not found!\n", lpszUrlName);
3664 SetLastError(ERROR_FILE_NOT_FOUND);
3668 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3670 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3675 /***********************************************************************
3676 * DeleteUrlCacheEntryW (WININET.@)
3679 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3681 URLCACHECONTAINER * pContainer;
3682 LPURLCACHE_HEADER pHeader;
3683 struct _HASH_ENTRY * pHashEntry;
3688 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3690 urlA = heap_strdupWtoA(lpszUrlName);
3693 SetLastError(ERROR_OUTOFMEMORY);
3697 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3698 if (error != ERROR_SUCCESS)
3701 SetLastError(error);
3705 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3706 if (error != ERROR_SUCCESS)
3709 SetLastError(error);
3713 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3719 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3721 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3722 TRACE("entry %s not found!\n", debugstr_a(urlA));
3724 SetLastError(ERROR_FILE_NOT_FOUND);
3728 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3730 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3735 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3737 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3741 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3743 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3747 /***********************************************************************
3748 * CreateCacheContainerA (WININET.@)
3750 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3751 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3753 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3754 d1, d2, d3, d4, d5, d6, d7, d8);
3758 /***********************************************************************
3759 * CreateCacheContainerW (WININET.@)
3761 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3762 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3764 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3765 d1, d2, d3, d4, d5, d6, d7, d8);
3769 /***********************************************************************
3770 * FindFirstUrlCacheContainerA (WININET.@)
3772 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3774 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3778 /***********************************************************************
3779 * FindFirstUrlCacheContainerW (WININET.@)
3781 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3783 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3787 /***********************************************************************
3788 * FindNextUrlCacheContainerA (WININET.@)
3790 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3792 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3796 /***********************************************************************
3797 * FindNextUrlCacheContainerW (WININET.@)
3799 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3801 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3805 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3806 LPCSTR lpszUrlSearchPattern,
3810 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3811 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3813 LPDWORD pcbReserved2,
3817 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3818 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3819 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3820 SetLastError(ERROR_FILE_NOT_FOUND);
3824 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3825 LPCWSTR lpszUrlSearchPattern,
3829 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3830 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3832 LPDWORD pcbReserved2,
3836 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3837 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3838 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3839 SetLastError(ERROR_FILE_NOT_FOUND);
3843 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3845 typedef struct URLCacheFindEntryHandle
3848 LPWSTR lpszUrlSearchPattern;
3849 DWORD dwContainerIndex;
3850 DWORD dwHashTableIndex;
3851 DWORD dwHashEntryIndex;
3852 } URLCacheFindEntryHandle;
3854 /***********************************************************************
3855 * FindFirstUrlCacheEntryA (WININET.@)
3858 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3859 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3861 URLCacheFindEntryHandle *pEntryHandle;
3863 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3865 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3869 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3870 if (lpszUrlSearchPattern)
3872 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3873 if (!pEntryHandle->lpszUrlSearchPattern)
3875 heap_free(pEntryHandle);
3880 pEntryHandle->lpszUrlSearchPattern = NULL;
3881 pEntryHandle->dwContainerIndex = 0;
3882 pEntryHandle->dwHashTableIndex = 0;
3883 pEntryHandle->dwHashEntryIndex = 0;
3885 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3887 heap_free(pEntryHandle);
3890 return pEntryHandle;
3893 /***********************************************************************
3894 * FindFirstUrlCacheEntryW (WININET.@)
3897 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3898 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3900 URLCacheFindEntryHandle *pEntryHandle;
3902 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3904 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3908 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3909 if (lpszUrlSearchPattern)
3911 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3912 if (!pEntryHandle->lpszUrlSearchPattern)
3914 heap_free(pEntryHandle);
3919 pEntryHandle->lpszUrlSearchPattern = NULL;
3920 pEntryHandle->dwContainerIndex = 0;
3921 pEntryHandle->dwHashTableIndex = 0;
3922 pEntryHandle->dwHashEntryIndex = 0;
3924 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3926 heap_free(pEntryHandle);
3929 return pEntryHandle;
3932 static BOOL FindNextUrlCacheEntryInternal(
3934 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3935 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3938 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3939 URLCACHECONTAINER * pContainer;
3941 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3943 SetLastError(ERROR_INVALID_HANDLE);
3947 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3948 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3950 LPURLCACHE_HEADER pHeader;
3951 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3954 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3955 if (error != ERROR_SUCCESS)
3957 SetLastError(error);
3961 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3964 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3965 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3967 const struct _HASH_ENTRY *pHashEntry = NULL;
3968 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3969 pEntryHandle->dwHashEntryIndex++)
3971 const URL_CACHEFILE_ENTRY *pUrlEntry;
3972 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3974 if (pEntry->dwSignature != URL_SIGNATURE)
3977 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3978 TRACE("Found URL: %s\n",
3979 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3980 TRACE("Header info: %s\n",
3981 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3983 error = URLCache_CopyEntry(
3986 lpNextCacheEntryInfo,
3987 lpdwNextCacheEntryInfoBufferSize,
3990 if (error != ERROR_SUCCESS)
3992 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3993 SetLastError(error);
3996 TRACE("Local File Name: %s\n",
3997 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3999 /* increment the current index so that next time the function
4000 * is called the next entry is returned */
4001 pEntryHandle->dwHashEntryIndex++;
4002 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4007 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4010 SetLastError(ERROR_NO_MORE_ITEMS);
4014 /***********************************************************************
4015 * FindNextUrlCacheEntryA (WININET.@)
4017 BOOL WINAPI FindNextUrlCacheEntryA(
4019 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
4020 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4022 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4024 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4025 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4028 /***********************************************************************
4029 * FindNextUrlCacheEntryW (WININET.@)
4031 BOOL WINAPI FindNextUrlCacheEntryW(
4033 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4034 LPDWORD lpdwNextCacheEntryInfoBufferSize
4037 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4039 return FindNextUrlCacheEntryInternal(hEnumHandle,
4040 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4041 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4044 /***********************************************************************
4045 * FindCloseUrlCache (WININET.@)
4047 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4049 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
4051 TRACE("(%p)\n", hEnumHandle);
4053 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4055 SetLastError(ERROR_INVALID_HANDLE);
4059 pEntryHandle->dwMagic = 0;
4060 heap_free(pEntryHandle->lpszUrlSearchPattern);
4061 heap_free(pEntryHandle);
4065 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4066 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4068 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4069 dwSearchCondition, lpGroupId, lpReserved);
4073 BOOL WINAPI FindNextUrlCacheEntryExA(
4075 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4076 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4078 LPDWORD pcbReserved2,
4082 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4083 lpReserved, pcbReserved2, lpReserved3);
4087 BOOL WINAPI FindNextUrlCacheEntryExW(
4089 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4090 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4092 LPDWORD pcbReserved2,
4096 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4097 lpReserved, pcbReserved2, lpReserved3);
4101 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4103 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4107 /***********************************************************************
4108 * CreateUrlCacheGroup (WININET.@)
4111 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4113 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4117 /***********************************************************************
4118 * DeleteUrlCacheGroup (WININET.@)
4121 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4123 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4124 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4128 /***********************************************************************
4129 * DeleteWpadCacheForNetworks (WININET.@)
4130 * Undocumented, added in IE8
4132 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
4134 FIXME("(%d) stub\n", unk1);
4138 /***********************************************************************
4139 * SetUrlCacheEntryGroupA (WININET.@)
4142 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4143 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4146 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4147 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4148 pbGroupAttributes, cbGroupAttributes, lpReserved);
4149 SetLastError(ERROR_FILE_NOT_FOUND);
4153 /***********************************************************************
4154 * SetUrlCacheEntryGroupW (WININET.@)
4157 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4158 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4161 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4162 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4163 pbGroupAttributes, cbGroupAttributes, lpReserved);
4164 SetLastError(ERROR_FILE_NOT_FOUND);
4168 /***********************************************************************
4169 * GetUrlCacheConfigInfoW (WININET.@)
4171 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4173 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4174 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4178 /***********************************************************************
4179 * GetUrlCacheConfigInfoA (WININET.@)
4181 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4183 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4184 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4188 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4189 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4190 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4192 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4193 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4194 lpdwGroupInfo, lpReserved);
4198 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4199 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4200 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4202 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4203 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4204 lpdwGroupInfo, lpReserved);
4208 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4209 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4211 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4212 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4216 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4217 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4219 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4220 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4224 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4226 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4230 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4232 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4236 /***********************************************************************
4237 * DeleteIE3Cache (WININET.@)
4239 * Deletes the files used by the IE3 URL caching system.
4242 * hWnd [I] A dummy window.
4243 * hInst [I] Instance of process calling the function.
4244 * lpszCmdLine [I] Options used by function.
4245 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4247 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4249 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4253 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
4254 FILETIME *pftLastModified)
4257 FILETIME now, expired;
4259 *pftLastModified = pUrlEntry->LastModifiedTime;
4260 GetSystemTimeAsFileTime(&now);
4261 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
4262 pUrlEntry->wExpiredTime, &expired);
4263 /* If the expired time is 0, it's interpreted as not expired */
4264 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4267 ret = CompareFileTime(&expired, &now) < 0;
4271 /***********************************************************************
4272 * IsUrlCacheEntryExpiredA (WININET.@)
4276 * dwFlags [I] Unknown
4277 * pftLastModified [O] Last modified time
4279 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4281 LPURLCACHE_HEADER pHeader;
4282 struct _HASH_ENTRY * pHashEntry;
4283 const CACHEFILE_ENTRY * pEntry;
4284 const URL_CACHEFILE_ENTRY * pUrlEntry;
4285 URLCACHECONTAINER * pContainer;
4288 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4290 if (!url || !pftLastModified)
4293 FIXME("unknown flags 0x%08x\n", dwFlags);
4295 /* Any error implies that the URL is expired, i.e. not in the cache */
4296 if (URLCacheContainers_FindContainerA(url, &pContainer))
4298 memset(pftLastModified, 0, sizeof(*pftLastModified));
4302 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4304 memset(pftLastModified, 0, sizeof(*pftLastModified));
4308 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4310 memset(pftLastModified, 0, sizeof(*pftLastModified));
4314 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4316 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4317 memset(pftLastModified, 0, sizeof(*pftLastModified));
4318 TRACE("entry %s not found!\n", url);
4322 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4323 if (pEntry->dwSignature != URL_SIGNATURE)
4325 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4326 memset(pftLastModified, 0, sizeof(*pftLastModified));
4327 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4331 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4332 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4334 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4339 /***********************************************************************
4340 * IsUrlCacheEntryExpiredW (WININET.@)
4344 * dwFlags [I] Unknown
4345 * pftLastModified [O] Last modified time
4347 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4349 LPURLCACHE_HEADER pHeader;
4350 struct _HASH_ENTRY * pHashEntry;
4351 const CACHEFILE_ENTRY * pEntry;
4352 const URL_CACHEFILE_ENTRY * pUrlEntry;
4353 URLCACHECONTAINER * pContainer;
4356 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4358 if (!url || !pftLastModified)
4361 FIXME("unknown flags 0x%08x\n", dwFlags);
4363 /* Any error implies that the URL is expired, i.e. not in the cache */
4364 if (URLCacheContainers_FindContainerW(url, &pContainer))
4366 memset(pftLastModified, 0, sizeof(*pftLastModified));
4370 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4372 memset(pftLastModified, 0, sizeof(*pftLastModified));
4376 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4378 memset(pftLastModified, 0, sizeof(*pftLastModified));
4382 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4384 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4385 memset(pftLastModified, 0, sizeof(*pftLastModified));
4386 TRACE("entry %s not found!\n", debugstr_w(url));
4390 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4392 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4393 memset(pftLastModified, 0, sizeof(*pftLastModified));
4394 TRACE("entry %s not found!\n", debugstr_w(url));
4398 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4399 if (pEntry->dwSignature != URL_SIGNATURE)
4401 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4402 memset(pftLastModified, 0, sizeof(*pftLastModified));
4403 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4407 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4408 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4410 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4415 /***********************************************************************
4416 * GetDiskInfoA (WININET.@)
4418 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4421 ULARGE_INTEGER bytes_free, bytes_total;
4423 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4427 SetLastError(ERROR_INVALID_PARAMETER);
4431 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4433 if (cluster_size) *cluster_size = 1;
4434 if (free) *free = bytes_free.QuadPart;
4435 if (total) *total = bytes_total.QuadPart;
4440 /***********************************************************************
4441 * RegisterUrlCacheNotification (WININET.@)
4443 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4445 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4449 /***********************************************************************
4450 * IncrementUrlCacheHeaderData (WININET.@)
4452 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4454 FIXME("(%u, %p)\n", index, data);
4458 /***********************************************************************
4459 * RunOnceUrlCache (WININET.@)
4462 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4464 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4468 BOOL init_urlcache(void)
4470 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4471 if(!dll_unload_event)
4474 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4475 if(!free_cache_running) {
4476 CloseHandle(dll_unload_event);
4480 URLCacheContainers_CreateDefaults();
4484 void free_urlcache(void)
4486 SetEvent(dll_unload_event);
4487 WaitForSingleObject(free_cache_running, INFINITE);
4488 ReleaseSemaphore(free_cache_running, 1, NULL);
4489 CloseHandle(free_cache_running);
4490 CloseHandle(dll_unload_event);
4492 URLCacheContainers_DeleteAll();