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))
2904 if (!lstrcmpW(lpszUrlPart, szWWW))
2906 lpszUrlPart += lstrlenW(szWWW);
2909 count = lpszUrlEnd - lpszUrlPart;
2911 if (bFound && (count < MAX_PATH))
2913 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2917 while(len && szFile[--len] == '/') szFile[len] = '\0';
2919 /* FIXME: get rid of illegal characters like \, / and : */
2920 TRACE("File name: %s\n", debugstr_a(szFile));
2924 generate_name = TRUE;
2928 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2929 if (error != ERROR_SUCCESS)
2931 SetLastError(error);
2935 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2936 if (error != ERROR_SUCCESS)
2938 SetLastError(error);
2942 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2945 if(pHeader->DirectoryCount)
2946 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2948 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2950 lBufferSize = MAX_PATH * sizeof(WCHAR);
2951 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2953 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2954 debugstr_a(szFile), lBufferSize);
2955 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2959 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2961 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2962 lpszFileNameNoPath >= lpszFileName;
2963 --lpszFileNameNoPath)
2965 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2969 countnoextension = lstrlenW(lpszFileNameNoPath);
2970 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2971 if (lpszFileNameExtension)
2972 countnoextension -= lstrlenW(lpszFileNameExtension);
2973 *szExtension = '\0';
2975 if (lpszFileExtension)
2977 szExtension[0] = '.';
2978 lstrcpyW(szExtension+1, lpszFileExtension);
2981 for (i = 0; i<255 && !generate_name; i++)
2983 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2986 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2987 for (p = lpszFileNameNoPath + 1; *p; p++)
2993 case '/': case '\\':
3000 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3002 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3003 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3004 if (hFile != INVALID_HANDLE_VALUE)
3011 /* Try to generate random name */
3012 GetSystemTimeAsFileTime(&ft);
3013 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
3015 for(i=0; i<255; i++)
3018 ULONGLONG n = ft.dwHighDateTime;
3020 n += ft.dwLowDateTime;
3021 n ^= (ULONGLONG)i<<48;
3027 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
3030 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3031 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3032 if (hFile != INVALID_HANDLE_VALUE)
3039 WARN("Could not find a unique filename\n");
3043 /***********************************************************************
3044 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3046 * The bug we are compensating for is that some drongo at Microsoft
3047 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3048 * As a consequence, CommitUrlCacheEntryA has been effectively
3049 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3050 * is still defined as LPCWSTR. The result (other than madness) is
3051 * that we always need to store lpHeaderInfo in CP_ACP rather than
3052 * in UTF16, and we need to avoid converting lpHeaderInfo in
3053 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3054 * result will lose data for arbitrary binary data.
3057 static BOOL CommitUrlCacheEntryInternal(
3058 IN LPCWSTR lpszUrlName,
3059 IN LPCWSTR lpszLocalFileName,
3060 IN FILETIME ExpireTime,
3061 IN FILETIME LastModifiedTime,
3062 IN DWORD CacheEntryType,
3063 IN LPBYTE lpHeaderInfo,
3064 IN DWORD dwHeaderSize,
3065 IN LPCWSTR lpszFileExtension,
3066 IN LPCWSTR lpszOriginalUrl
3069 URLCACHECONTAINER * pContainer;
3070 LPURLCACHE_HEADER pHeader;
3071 struct _HASH_ENTRY * pHashEntry;
3072 CACHEFILE_ENTRY * pEntry;
3073 URL_CACHEFILE_ENTRY * pUrlEntry;
3074 DWORD url_entry_offset;
3075 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3076 DWORD dwOffsetLocalFileName = 0;
3077 DWORD dwOffsetHeader = 0;
3078 DWORD dwOffsetFileExtension = 0;
3079 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3080 LARGE_INTEGER file_size;
3082 char achFile[MAX_PATH];
3083 LPSTR lpszUrlNameA = NULL;
3084 LPSTR lpszFileExtensionA = NULL;
3085 char *pchLocalFileName = 0;
3087 DWORD exempt_delta = 0;
3090 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3091 debugstr_w(lpszUrlName),
3092 debugstr_w(lpszLocalFileName),
3096 debugstr_w(lpszFileExtension),
3097 debugstr_w(lpszOriginalUrl));
3099 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3101 SetLastError(ERROR_INVALID_PARAMETER);
3104 if (lpszOriginalUrl)
3105 WARN(": lpszOriginalUrl ignored\n");
3107 memset(&file_attr, 0, sizeof(file_attr));
3108 if (lpszLocalFileName)
3110 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3113 file_size.u.LowPart = file_attr.nFileSizeLow;
3114 file_size.u.HighPart = file_attr.nFileSizeHigh;
3116 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3117 if (error != ERROR_SUCCESS)
3119 SetLastError(error);
3123 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3124 if (error != ERROR_SUCCESS)
3126 SetLastError(error);
3130 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3133 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3136 error = GetLastError();
3140 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3142 error = GetLastError();
3146 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3148 URL_CACHEFILE_ENTRY *pUrlEntry = (URL_CACHEFILE_ENTRY*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3149 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3151 TRACE("Trying to overwrite locked entry\n");
3152 error = ERROR_SHARING_VIOLATION;
3156 hit_rate = pUrlEntry->dwHitRate;
3157 exempt_delta = pUrlEntry->dwExemptDelta;
3158 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3161 if (pHeader->DirectoryCount)
3164 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3166 if (lpszLocalFileName)
3168 BOOL bFound = FALSE;
3170 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3172 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3173 error = ERROR_INVALID_PARAMETER;
3177 /* skip container path prefix */
3178 lpszLocalFileName += lstrlenW(pContainer->path);
3180 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3181 pchLocalFileName = achFile;
3183 if(pHeader->DirectoryCount)
3185 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
3187 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
3196 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3197 error = ERROR_INVALID_PARAMETER;
3201 lpszLocalFileName += DIR_LENGTH + 1;
3202 pchLocalFileName += DIR_LENGTH + 1;
3206 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3207 if (lpszLocalFileName)
3209 dwOffsetLocalFileName = dwBytesNeeded;
3210 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3214 dwOffsetHeader = dwBytesNeeded;
3215 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3217 if (lpszFileExtensionA)
3219 dwOffsetFileExtension = dwBytesNeeded;
3220 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3223 /* round up to next block */
3224 if (dwBytesNeeded % BLOCKSIZE)
3226 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3227 dwBytesNeeded += BLOCKSIZE;
3230 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3231 while (error == ERROR_HANDLE_DISK_FULL)
3233 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3234 if (error == ERROR_SUCCESS)
3235 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3237 if (error != ERROR_SUCCESS)
3240 /* FindFirstFreeEntry fills in blocks used */
3241 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3242 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3243 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
3244 pUrlEntry->CacheDir = cDirectory;
3245 pUrlEntry->CacheEntryType = CacheEntryType | pContainer->default_entry_type;
3246 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
3247 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3249 /* Sticky entries have a default exempt time of one day */
3250 exempt_delta = 86400;
3252 pUrlEntry->dwExemptDelta = exempt_delta;
3253 pUrlEntry->dwHitRate = hit_rate+1;
3254 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
3255 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
3256 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
3257 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
3258 pUrlEntry->size.QuadPart = file_size.QuadPart;
3259 pUrlEntry->dwUseCount = 0;
3260 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
3261 pUrlEntry->LastModifiedTime = LastModifiedTime;
3262 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
3263 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
3264 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->LastWriteDate, &pUrlEntry->LastWriteTime);
3267 pUrlEntry->dwUnknown1 = 0;
3268 pUrlEntry->dwUnknown2 = 0;
3269 pUrlEntry->dwUnknown3 = 0x60;
3270 pUrlEntry->Unknown4 = 0;
3271 pUrlEntry->wUnknown5 = 0x1010;
3272 pUrlEntry->dwUnknown7 = 0;
3273 pUrlEntry->dwUnknown8 = 0;
3276 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
3277 if (dwOffsetLocalFileName)
3278 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3280 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3281 if (dwOffsetFileExtension)
3282 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3284 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3285 while (error == ERROR_HANDLE_DISK_FULL)
3287 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3288 if (error == ERROR_SUCCESS)
3290 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
3291 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3292 url_entry_offset, HASHTABLE_URL);
3295 if (error != ERROR_SUCCESS)
3296 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
3299 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3300 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
3301 if (CacheEntryType & STICKY_CACHE_ENTRY)
3302 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
3304 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
3305 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
3306 pHeader->CacheLimit.QuadPart)
3307 handle_full_cache();
3311 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3312 heap_free(lpszUrlNameA);
3313 heap_free(lpszFileExtensionA);
3315 if (error == ERROR_SUCCESS)
3319 SetLastError(error);
3324 /***********************************************************************
3325 * CommitUrlCacheEntryA (WININET.@)
3328 BOOL WINAPI CommitUrlCacheEntryA(
3329 IN LPCSTR lpszUrlName,
3330 IN LPCSTR lpszLocalFileName,
3331 IN FILETIME ExpireTime,
3332 IN FILETIME LastModifiedTime,
3333 IN DWORD CacheEntryType,
3334 IN LPBYTE lpHeaderInfo,
3335 IN DWORD dwHeaderSize,
3336 IN LPCSTR lpszFileExtension,
3337 IN LPCSTR lpszOriginalUrl
3340 WCHAR *url_name = NULL;
3341 WCHAR *local_file_name = NULL;
3342 WCHAR *original_url = NULL;
3343 WCHAR *file_extension = NULL;
3344 BOOL bSuccess = FALSE;
3346 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3347 debugstr_a(lpszUrlName),
3348 debugstr_a(lpszLocalFileName),
3352 debugstr_a(lpszFileExtension),
3353 debugstr_a(lpszOriginalUrl));
3355 url_name = heap_strdupAtoW(lpszUrlName);
3359 if (lpszLocalFileName)
3361 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3362 if (!local_file_name)
3365 if (lpszFileExtension)
3367 file_extension = heap_strdupAtoW(lpszFileExtension);
3368 if (!file_extension)
3371 if (lpszOriginalUrl)
3373 original_url = heap_strdupAtoW(lpszOriginalUrl);
3378 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3379 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3380 file_extension, original_url);
3383 heap_free(original_url);
3384 heap_free(file_extension);
3385 heap_free(local_file_name);
3386 heap_free(url_name);
3390 /***********************************************************************
3391 * CommitUrlCacheEntryW (WININET.@)
3394 BOOL WINAPI CommitUrlCacheEntryW(
3395 IN LPCWSTR lpszUrlName,
3396 IN LPCWSTR lpszLocalFileName,
3397 IN FILETIME ExpireTime,
3398 IN FILETIME LastModifiedTime,
3399 IN DWORD CacheEntryType,
3400 IN LPWSTR lpHeaderInfo,
3401 IN DWORD dwHeaderSize,
3402 IN LPCWSTR lpszFileExtension,
3403 IN LPCWSTR lpszOriginalUrl
3407 BOOL bSuccess = FALSE;
3409 CHAR *header_info = NULL;
3411 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3412 debugstr_w(lpszUrlName),
3413 debugstr_w(lpszLocalFileName),
3417 debugstr_w(lpszFileExtension),
3418 debugstr_w(lpszOriginalUrl));
3420 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3423 len = strlen(header_info);
3424 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3425 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3431 dwError = GetLastError();
3435 heap_free(header_info);
3437 SetLastError(dwError);
3443 /***********************************************************************
3444 * ReadUrlCacheEntryStream (WININET.@)
3447 BOOL WINAPI ReadUrlCacheEntryStream(
3448 IN HANDLE hUrlCacheStream,
3449 IN DWORD dwLocation,
3450 IN OUT LPVOID lpBuffer,
3451 IN OUT LPDWORD lpdwLen,
3455 /* Get handle to file from 'stream' */
3456 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3458 if (dwReserved != 0)
3460 ERR("dwReserved != 0\n");
3461 SetLastError(ERROR_INVALID_PARAMETER);
3465 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3467 SetLastError(ERROR_INVALID_HANDLE);
3471 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3473 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3476 /***********************************************************************
3477 * RetrieveUrlCacheEntryStreamA (WININET.@)
3480 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3481 IN LPCSTR lpszUrlName,
3482 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3483 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3484 IN BOOL fRandomRead,
3488 /* NOTE: this is not the same as the way that the native
3489 * version allocates 'stream' handles. I did it this way
3490 * as it is much easier and no applications should depend
3491 * on this behaviour. (Native version appears to allocate
3492 * indices into a table)
3494 STREAM_HANDLE * pStream;
3497 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3498 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3500 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3502 lpdwCacheEntryInfoBufferSize,
3508 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3513 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3515 if (hFile == INVALID_HANDLE_VALUE)
3518 /* allocate handle storage space */
3519 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3523 SetLastError(ERROR_OUTOFMEMORY);
3527 pStream->hFile = hFile;
3528 strcpy(pStream->lpszUrl, lpszUrlName);
3532 /***********************************************************************
3533 * RetrieveUrlCacheEntryStreamW (WININET.@)
3536 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3537 IN LPCWSTR lpszUrlName,
3538 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3539 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3540 IN BOOL fRandomRead,
3546 /* NOTE: this is not the same as the way that the native
3547 * version allocates 'stream' handles. I did it this way
3548 * as it is much easier and no applications should depend
3549 * on this behaviour. (Native version appears to allocate
3550 * indices into a table)
3552 STREAM_HANDLE * pStream;
3555 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3556 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3558 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3560 lpdwCacheEntryInfoBufferSize,
3566 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3571 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3573 if (hFile == INVALID_HANDLE_VALUE)
3576 /* allocate handle storage space */
3577 size = sizeof(STREAM_HANDLE);
3578 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3580 pStream = heap_alloc(size);
3584 SetLastError(ERROR_OUTOFMEMORY);
3588 pStream->hFile = hFile;
3589 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3593 /***********************************************************************
3594 * UnlockUrlCacheEntryStream (WININET.@)
3597 BOOL WINAPI UnlockUrlCacheEntryStream(
3598 IN HANDLE hUrlCacheStream,
3602 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3604 if (dwReserved != 0)
3606 ERR("dwReserved != 0\n");
3607 SetLastError(ERROR_INVALID_PARAMETER);
3611 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3613 SetLastError(ERROR_INVALID_HANDLE);
3617 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3620 CloseHandle(pStream->hFile);
3626 /***********************************************************************
3627 * DeleteUrlCacheEntryA (WININET.@)
3630 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3632 URLCACHECONTAINER * pContainer;
3633 LPURLCACHE_HEADER pHeader;
3634 struct _HASH_ENTRY * pHashEntry;
3638 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3640 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3641 if (error != ERROR_SUCCESS)
3643 SetLastError(error);
3647 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3648 if (error != ERROR_SUCCESS)
3650 SetLastError(error);
3654 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3657 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3659 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3660 TRACE("entry %s not found!\n", lpszUrlName);
3661 SetLastError(ERROR_FILE_NOT_FOUND);
3665 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3667 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3672 /***********************************************************************
3673 * DeleteUrlCacheEntryW (WININET.@)
3676 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3678 URLCACHECONTAINER * pContainer;
3679 LPURLCACHE_HEADER pHeader;
3680 struct _HASH_ENTRY * pHashEntry;
3685 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3687 urlA = heap_strdupWtoA(lpszUrlName);
3690 SetLastError(ERROR_OUTOFMEMORY);
3694 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3695 if (error != ERROR_SUCCESS)
3698 SetLastError(error);
3702 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3703 if (error != ERROR_SUCCESS)
3706 SetLastError(error);
3710 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3716 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3718 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3719 TRACE("entry %s not found!\n", debugstr_a(urlA));
3721 SetLastError(ERROR_FILE_NOT_FOUND);
3725 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3727 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3732 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3734 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3738 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3740 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3744 /***********************************************************************
3745 * CreateCacheContainerA (WININET.@)
3747 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3748 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3750 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3751 d1, d2, d3, d4, d5, d6, d7, d8);
3755 /***********************************************************************
3756 * CreateCacheContainerW (WININET.@)
3758 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3759 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3761 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3762 d1, d2, d3, d4, d5, d6, d7, d8);
3766 /***********************************************************************
3767 * FindFirstUrlCacheContainerA (WININET.@)
3769 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3771 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3775 /***********************************************************************
3776 * FindFirstUrlCacheContainerW (WININET.@)
3778 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3780 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3784 /***********************************************************************
3785 * FindNextUrlCacheContainerA (WININET.@)
3787 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3789 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3793 /***********************************************************************
3794 * FindNextUrlCacheContainerW (WININET.@)
3796 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3798 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3802 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3803 LPCSTR lpszUrlSearchPattern,
3807 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3808 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3810 LPDWORD pcbReserved2,
3814 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3815 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3816 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3817 SetLastError(ERROR_FILE_NOT_FOUND);
3821 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3822 LPCWSTR lpszUrlSearchPattern,
3826 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3827 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3829 LPDWORD pcbReserved2,
3833 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3834 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3835 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3836 SetLastError(ERROR_FILE_NOT_FOUND);
3840 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3842 typedef struct URLCacheFindEntryHandle
3845 LPWSTR lpszUrlSearchPattern;
3846 DWORD dwContainerIndex;
3847 DWORD dwHashTableIndex;
3848 DWORD dwHashEntryIndex;
3849 } URLCacheFindEntryHandle;
3851 /***********************************************************************
3852 * FindFirstUrlCacheEntryA (WININET.@)
3855 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3856 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3858 URLCacheFindEntryHandle *pEntryHandle;
3860 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3862 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3866 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3867 if (lpszUrlSearchPattern)
3869 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3870 if (!pEntryHandle->lpszUrlSearchPattern)
3872 heap_free(pEntryHandle);
3877 pEntryHandle->lpszUrlSearchPattern = NULL;
3878 pEntryHandle->dwContainerIndex = 0;
3879 pEntryHandle->dwHashTableIndex = 0;
3880 pEntryHandle->dwHashEntryIndex = 0;
3882 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3884 heap_free(pEntryHandle);
3887 return pEntryHandle;
3890 /***********************************************************************
3891 * FindFirstUrlCacheEntryW (WININET.@)
3894 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3895 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3897 URLCacheFindEntryHandle *pEntryHandle;
3899 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3901 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3905 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3906 if (lpszUrlSearchPattern)
3908 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3909 if (!pEntryHandle->lpszUrlSearchPattern)
3911 heap_free(pEntryHandle);
3916 pEntryHandle->lpszUrlSearchPattern = NULL;
3917 pEntryHandle->dwContainerIndex = 0;
3918 pEntryHandle->dwHashTableIndex = 0;
3919 pEntryHandle->dwHashEntryIndex = 0;
3921 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3923 heap_free(pEntryHandle);
3926 return pEntryHandle;
3929 static BOOL FindNextUrlCacheEntryInternal(
3931 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3932 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3935 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3936 URLCACHECONTAINER * pContainer;
3938 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3940 SetLastError(ERROR_INVALID_HANDLE);
3944 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3945 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3947 LPURLCACHE_HEADER pHeader;
3948 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3951 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3952 if (error != ERROR_SUCCESS)
3954 SetLastError(error);
3958 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3961 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3962 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3964 const struct _HASH_ENTRY *pHashEntry = NULL;
3965 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3966 pEntryHandle->dwHashEntryIndex++)
3968 const URL_CACHEFILE_ENTRY *pUrlEntry;
3969 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3971 if (pEntry->dwSignature != URL_SIGNATURE)
3974 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3975 TRACE("Found URL: %s\n",
3976 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3977 TRACE("Header info: %s\n",
3978 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3980 error = URLCache_CopyEntry(
3983 lpNextCacheEntryInfo,
3984 lpdwNextCacheEntryInfoBufferSize,
3987 if (error != ERROR_SUCCESS)
3989 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3990 SetLastError(error);
3993 TRACE("Local File Name: %s\n",
3994 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3996 /* increment the current index so that next time the function
3997 * is called the next entry is returned */
3998 pEntryHandle->dwHashEntryIndex++;
3999 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4004 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4007 SetLastError(ERROR_NO_MORE_ITEMS);
4011 /***********************************************************************
4012 * FindNextUrlCacheEntryA (WININET.@)
4014 BOOL WINAPI FindNextUrlCacheEntryA(
4016 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
4017 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4019 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4021 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4022 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4025 /***********************************************************************
4026 * FindNextUrlCacheEntryW (WININET.@)
4028 BOOL WINAPI FindNextUrlCacheEntryW(
4030 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4031 LPDWORD lpdwNextCacheEntryInfoBufferSize
4034 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4036 return FindNextUrlCacheEntryInternal(hEnumHandle,
4037 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4038 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4041 /***********************************************************************
4042 * FindCloseUrlCache (WININET.@)
4044 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4046 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
4048 TRACE("(%p)\n", hEnumHandle);
4050 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4052 SetLastError(ERROR_INVALID_HANDLE);
4056 pEntryHandle->dwMagic = 0;
4057 heap_free(pEntryHandle->lpszUrlSearchPattern);
4058 heap_free(pEntryHandle);
4062 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4063 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4065 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4066 dwSearchCondition, lpGroupId, lpReserved);
4070 BOOL WINAPI FindNextUrlCacheEntryExA(
4072 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4073 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4075 LPDWORD pcbReserved2,
4079 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4080 lpReserved, pcbReserved2, lpReserved3);
4084 BOOL WINAPI FindNextUrlCacheEntryExW(
4086 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4087 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4089 LPDWORD pcbReserved2,
4093 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4094 lpReserved, pcbReserved2, lpReserved3);
4098 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4100 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4104 /***********************************************************************
4105 * CreateUrlCacheGroup (WININET.@)
4108 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4110 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4114 /***********************************************************************
4115 * DeleteUrlCacheGroup (WININET.@)
4118 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4120 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4121 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4125 /***********************************************************************
4126 * SetUrlCacheEntryGroupA (WININET.@)
4129 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4130 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4133 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4134 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4135 pbGroupAttributes, cbGroupAttributes, lpReserved);
4136 SetLastError(ERROR_FILE_NOT_FOUND);
4140 /***********************************************************************
4141 * SetUrlCacheEntryGroupW (WININET.@)
4144 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4145 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4148 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4149 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4150 pbGroupAttributes, cbGroupAttributes, lpReserved);
4151 SetLastError(ERROR_FILE_NOT_FOUND);
4155 /***********************************************************************
4156 * GetUrlCacheConfigInfoW (WININET.@)
4158 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4160 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4161 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4165 /***********************************************************************
4166 * GetUrlCacheConfigInfoA (WININET.@)
4168 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4170 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4171 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4175 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4176 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4177 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4179 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4180 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4181 lpdwGroupInfo, lpReserved);
4185 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4186 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4187 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4189 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4190 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4191 lpdwGroupInfo, lpReserved);
4195 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4196 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4198 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4199 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4203 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4204 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4206 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4207 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4211 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4213 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4217 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4219 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4223 /***********************************************************************
4224 * DeleteIE3Cache (WININET.@)
4226 * Deletes the files used by the IE3 URL caching system.
4229 * hWnd [I] A dummy window.
4230 * hInst [I] Instance of process calling the function.
4231 * lpszCmdLine [I] Options used by function.
4232 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4234 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4236 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4240 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
4241 FILETIME *pftLastModified)
4244 FILETIME now, expired;
4246 *pftLastModified = pUrlEntry->LastModifiedTime;
4247 GetSystemTimeAsFileTime(&now);
4248 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
4249 pUrlEntry->wExpiredTime, &expired);
4250 /* If the expired time is 0, it's interpreted as not expired */
4251 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4254 ret = CompareFileTime(&expired, &now) < 0;
4258 /***********************************************************************
4259 * IsUrlCacheEntryExpiredA (WININET.@)
4263 * dwFlags [I] Unknown
4264 * pftLastModified [O] Last modified time
4266 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4268 LPURLCACHE_HEADER pHeader;
4269 struct _HASH_ENTRY * pHashEntry;
4270 const CACHEFILE_ENTRY * pEntry;
4271 const URL_CACHEFILE_ENTRY * pUrlEntry;
4272 URLCACHECONTAINER * pContainer;
4275 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4277 if (!url || !pftLastModified)
4280 FIXME("unknown flags 0x%08x\n", dwFlags);
4282 /* Any error implies that the URL is expired, i.e. not in the cache */
4283 if (URLCacheContainers_FindContainerA(url, &pContainer))
4285 memset(pftLastModified, 0, sizeof(*pftLastModified));
4289 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4291 memset(pftLastModified, 0, sizeof(*pftLastModified));
4295 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4297 memset(pftLastModified, 0, sizeof(*pftLastModified));
4301 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4303 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4304 memset(pftLastModified, 0, sizeof(*pftLastModified));
4305 TRACE("entry %s not found!\n", url);
4309 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4310 if (pEntry->dwSignature != URL_SIGNATURE)
4312 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4313 memset(pftLastModified, 0, sizeof(*pftLastModified));
4314 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4318 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4319 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4321 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4326 /***********************************************************************
4327 * IsUrlCacheEntryExpiredW (WININET.@)
4331 * dwFlags [I] Unknown
4332 * pftLastModified [O] Last modified time
4334 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4336 LPURLCACHE_HEADER pHeader;
4337 struct _HASH_ENTRY * pHashEntry;
4338 const CACHEFILE_ENTRY * pEntry;
4339 const URL_CACHEFILE_ENTRY * pUrlEntry;
4340 URLCACHECONTAINER * pContainer;
4343 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4345 if (!url || !pftLastModified)
4348 FIXME("unknown flags 0x%08x\n", dwFlags);
4350 /* Any error implies that the URL is expired, i.e. not in the cache */
4351 if (URLCacheContainers_FindContainerW(url, &pContainer))
4353 memset(pftLastModified, 0, sizeof(*pftLastModified));
4357 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4359 memset(pftLastModified, 0, sizeof(*pftLastModified));
4363 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4365 memset(pftLastModified, 0, sizeof(*pftLastModified));
4369 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4371 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4372 memset(pftLastModified, 0, sizeof(*pftLastModified));
4373 TRACE("entry %s not found!\n", debugstr_w(url));
4377 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4379 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4380 memset(pftLastModified, 0, sizeof(*pftLastModified));
4381 TRACE("entry %s not found!\n", debugstr_w(url));
4385 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4386 if (pEntry->dwSignature != URL_SIGNATURE)
4388 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4389 memset(pftLastModified, 0, sizeof(*pftLastModified));
4390 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4394 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4395 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4397 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4402 /***********************************************************************
4403 * GetDiskInfoA (WININET.@)
4405 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4408 ULARGE_INTEGER bytes_free, bytes_total;
4410 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4414 SetLastError(ERROR_INVALID_PARAMETER);
4418 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4420 if (cluster_size) *cluster_size = 1;
4421 if (free) *free = bytes_free.QuadPart;
4422 if (total) *total = bytes_total.QuadPart;
4427 /***********************************************************************
4428 * RegisterUrlCacheNotification (WININET.@)
4430 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4432 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4436 /***********************************************************************
4437 * IncrementUrlCacheHeaderData (WININET.@)
4439 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4441 FIXME("(%u, %p)\n", index, data);
4445 /***********************************************************************
4446 * RunOnceUrlCache (WININET.@)
4449 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4451 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4455 BOOL init_urlcache(void)
4457 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4458 if(!dll_unload_event)
4461 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4462 if(!free_cache_running) {
4463 CloseHandle(dll_unload_event);
4467 URLCacheContainers_CreateDefaults();
4471 void free_urlcache(void)
4473 SetEvent(dll_unload_event);
4474 WaitForSingleObject(free_cache_running, INFINITE);
4475 ReleaseSemaphore(free_cache_running, 1, NULL);
4476 CloseHandle(free_cache_running);
4477 CloseHandle(dll_unload_event);
4479 URLCacheContainers_DeleteAll();