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 #define ENTRY_START_OFFSET 0x4000
66 #define HASHTABLE_SIZE 448
67 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
68 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
71 #define NEWFILE_NUM_BLOCKS 0xd80
72 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
74 #define HASHTABLE_URL 0
75 #define HASHTABLE_DEL 1
76 #define HASHTABLE_LOCK 2
77 #define HASHTABLE_FREE 3
78 #define HASHTABLE_REDR 5
79 #define HASHTABLE_FLAG_BITS 4
81 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
82 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
83 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
84 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
85 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
87 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
89 typedef struct _CACHEFILE_ENTRY
93 DWORD dwSignature; /* e.g. "URL " */
94 /* CHAR szSignature[4];
96 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
99 typedef struct _URL_CACHEFILE_ENTRY
101 CACHEFILE_ENTRY CacheFileEntry;
102 FILETIME LastModifiedTime;
103 FILETIME LastAccessTime;
104 WORD wExpiredDate; /* expire date in dos format */
105 WORD wExpiredTime; /* expire time in dos format */
106 DWORD dwUnknown1; /* usually zero */
107 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
108 DWORD dwUnknown2; /* usually zero */
109 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
110 DWORD dwUnknown3; /* usually 0x60 */
111 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
112 BYTE CacheDir; /* index of cache directory this url is stored in */
113 BYTE Unknown4; /* usually zero */
114 WORD wUnknown5; /* usually 0x1010 */
115 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
116 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
117 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
118 DWORD dwHeaderInfoSize;
119 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
120 WORD wLastSyncDate; /* last sync date in dos format */
121 WORD wLastSyncTime; /* last sync time in dos format */
122 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
123 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
124 WORD wUnknownDate; /* usually same as wLastSyncDate */
125 WORD wUnknownTime; /* usually same as wLastSyncTime */
126 DWORD dwUnknown7; /* usually zero */
127 DWORD dwUnknown8; /* usually zero */
128 /* packing to dword align start of next field */
129 /* CHAR szSourceUrlName[]; (url) */
130 /* packing to dword align start of next field */
131 /* CHAR szLocalFileName[]; (local file name excluding path) */
132 /* packing to dword align start of next field */
133 /* CHAR szHeaderInfo[]; (header info) */
134 } URL_CACHEFILE_ENTRY;
142 typedef struct _HASH_CACHEFILE_ENTRY
144 CACHEFILE_ENTRY CacheFileEntry;
146 DWORD dwHashTableNumber;
147 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
148 } HASH_CACHEFILE_ENTRY;
150 typedef struct _DIRECTORY_DATA
153 char filename[DIR_LENGTH];
156 typedef struct _URLCACHE_HEADER
158 char szSignature[28];
160 DWORD dwOffsetFirstHashTable;
161 DWORD dwIndexCapacityInBlocks;
164 ULARGE_INTEGER CacheLimit;
165 ULARGE_INTEGER CacheUsage;
166 ULARGE_INTEGER ExemptUsage;
167 DWORD DirectoryCount; /* number of directory_data's */
168 DIRECTORY_DATA directory_data[1]; /* first directory entry */
169 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
170 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
172 typedef struct _STREAM_HANDLE
178 typedef struct _URLCACHECONTAINER
180 struct list entry; /* part of a list */
181 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
182 LPWSTR path; /* path to url container directory */
183 HANDLE hMapping; /* handle of file mapping */
184 DWORD file_size; /* size of file when mapping was opened */
185 HANDLE hMutex; /* handle of mutex */
189 /* List of all containers available */
190 static struct list UrlContainers = LIST_INIT(UrlContainers);
192 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
194 /***********************************************************************
195 * URLCache_PathToObjectName (Internal)
197 * Converts a path to a name suitable for use as a Win32 object name.
198 * Replaces '\\' characters in-place with the specified character
199 * (usually '_' or '!')
205 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
207 for (; *lpszPath; lpszPath++)
209 if (*lpszPath == '\\')
214 /***********************************************************************
215 * URLCacheContainer_OpenIndex (Internal)
217 * Opens the index file and saves mapping handle in hCacheIndexMapping
220 * ERROR_SUCCESS if succeeded
221 * Any other Win32 error code if failed
224 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
227 WCHAR wszFilePath[MAX_PATH];
230 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
231 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
233 WaitForSingleObject(pContainer->hMutex, INFINITE);
235 if (pContainer->hMapping) {
236 ReleaseMutex(pContainer->hMutex);
237 return ERROR_SUCCESS;
240 strcpyW(wszFilePath, pContainer->path);
241 strcatW(wszFilePath, wszIndex);
243 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
244 if (hFile == INVALID_HANDLE_VALUE)
246 /* Maybe the directory wasn't there? Try to create it */
247 if (CreateDirectoryW(pContainer->path, 0))
248 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
250 if (hFile == INVALID_HANDLE_VALUE)
252 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
253 ReleaseMutex(pContainer->hMutex);
254 return GetLastError();
257 dwFileSize = GetFileSize(hFile, NULL);
258 if (dwFileSize == INVALID_FILE_SIZE)
260 ReleaseMutex(pContainer->hMutex);
261 return GetLastError();
266 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
268 char achZeroes[0x1000];
270 DWORD dwError = ERROR_SUCCESS;
272 /* Write zeroes to the entire file so we can safely map it without
273 * fear of getting a SEGV because the disk is full.
275 memset(achZeroes, 0, sizeof(achZeroes));
276 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
278 DWORD dwWrite = sizeof(achZeroes);
281 if (NEWFILE_SIZE - dwOffset < dwWrite)
282 dwWrite = NEWFILE_SIZE - dwOffset;
283 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
284 dwWritten != dwWrite)
286 /* If we fail to write, we need to return the error that
287 * cause the problem and also make sure the file is no
288 * longer there, if possible.
290 dwError = GetLastError();
296 if (dwError == ERROR_SUCCESS)
298 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
302 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
307 WCHAR wszDirPath[MAX_PATH];
310 HASH_CACHEFILE_ENTRY *pHashEntry;
312 dwFileSize = NEWFILE_SIZE;
314 /* First set some constants and defaults in the header */
315 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
316 pHeader->dwFileSize = dwFileSize;
317 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
318 /* 127MB - taken from default for Windows 2000 */
319 pHeader->CacheLimit.QuadPart = 0x07ff5400;
320 /* Copied from a Windows 2000 cache index */
321 pHeader->DirectoryCount = 4;
323 /* If the registry has a cache size set, use the registry value */
324 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
327 DWORD len = sizeof(dw);
330 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
331 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
332 keytype == REG_DWORD)
334 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
339 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
341 /* Last step - create the directories */
343 strcpyW(wszDirPath, pContainer->path);
344 pwchDir = wszDirPath + strlenW(wszDirPath);
347 GetSystemTimeAsFileTime(&ft);
349 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
351 pHeader->directory_data[i].dwNumFiles = 0;
355 ULONGLONG n = ft.dwHighDateTime;
357 /* Generate a file name to attempt to create.
358 * This algorithm will create what will appear
359 * to be random and unrelated directory names
360 * of up to 9 characters in length.
363 n += ft.dwLowDateTime;
364 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
366 for (k = 0; k < 8; ++k)
370 /* Dividing by a prime greater than 36 helps
371 * with the appearance of randomness
376 pwchDir[k] = '0' + r;
378 pwchDir[k] = 'A' + (r - 10);
381 if (CreateDirectoryW(wszDirPath, 0))
383 /* The following is OK because we generated an
384 * 8 character directory name made from characters
385 * [A-Z0-9], which are equivalent for all code
386 * pages and for UTF-16
388 for (k = 0; k < 8; ++k)
389 pHeader->directory_data[i].filename[k] = pwchDir[k];
394 /* Give up. The most likely cause of this
395 * is a full disk, but whatever the cause
396 * is, it should be more than apparent that
399 dwError = GetLastError();
405 UnmapViewOfFile(pHeader);
409 dwError = GetLastError();
411 CloseHandle(hMapping);
415 dwError = GetLastError();
422 DeleteFileW(wszFilePath);
423 ReleaseMutex(pContainer->hMutex);
429 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
430 URLCache_PathToObjectName(wszFilePath, '_');
431 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
432 if (!pContainer->hMapping)
433 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
435 if (!pContainer->hMapping)
437 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
438 ReleaseMutex(pContainer->hMutex);
439 return GetLastError();
442 ReleaseMutex(pContainer->hMutex);
444 return ERROR_SUCCESS;
447 /***********************************************************************
448 * URLCacheContainer_CloseIndex (Internal)
456 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
458 CloseHandle(pContainer->hMapping);
459 pContainer->hMapping = NULL;
462 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
464 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
465 int cache_prefix_len = strlenW(cache_prefix);
472 pContainer->hMapping = NULL;
473 pContainer->file_size = 0;
475 pContainer->path = heap_strdupW(path);
476 if (!pContainer->path)
478 heap_free(pContainer);
482 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
483 if (!pContainer->cache_prefix)
485 heap_free(pContainer->path);
486 heap_free(pContainer);
490 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
492 CharLowerW(mutex_name);
493 URLCache_PathToObjectName(mutex_name, '!');
495 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
497 ERR("couldn't create mutex (error is %d)\n", GetLastError());
498 heap_free(pContainer->path);
499 heap_free(pContainer);
503 list_add_head(&UrlContainers, &pContainer->entry);
508 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
510 list_remove(&pContainer->entry);
512 URLCacheContainer_CloseIndex(pContainer);
513 CloseHandle(pContainer->hMutex);
514 heap_free(pContainer->path);
515 heap_free(pContainer->cache_prefix);
516 heap_free(pContainer);
519 void URLCacheContainers_CreateDefaults(void)
521 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
522 static const WCHAR UrlPrefix[] = {0};
523 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
524 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
525 static const WCHAR CookieSuffix[] = {0};
526 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
529 int nFolder; /* CSIDL_* constant */
530 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
531 const WCHAR * cache_prefix; /* prefix used to reference the container */
532 } DefaultContainerData[] =
534 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
535 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
536 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
540 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
542 WCHAR wszCachePath[MAX_PATH];
543 WCHAR wszMutexName[MAX_PATH];
544 int path_len, suffix_len;
546 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
548 ERR("Couldn't get path for default container %u\n", i);
551 path_len = strlenW(wszCachePath);
552 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
554 if (path_len + suffix_len + 2 > MAX_PATH)
556 ERR("Path too long\n");
560 wszCachePath[path_len] = '\\';
561 wszCachePath[path_len+1] = 0;
563 strcpyW(wszMutexName, wszCachePath);
567 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
568 wszCachePath[path_len + suffix_len + 1] = '\\';
569 wszCachePath[path_len + suffix_len + 2] = '\0';
572 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
576 void URLCacheContainers_DeleteAll(void)
578 while(!list_empty(&UrlContainers))
579 URLCacheContainer_DeleteContainer(
580 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
584 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
586 URLCACHECONTAINER * pContainer;
588 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
591 return ERROR_INVALID_PARAMETER;
593 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
595 int prefix_len = strlenW(pContainer->cache_prefix);
596 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
598 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
599 *ppContainer = pContainer;
600 return ERROR_SUCCESS;
603 ERR("no container found\n");
604 return ERROR_FILE_NOT_FOUND;
607 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
612 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
613 return ERROR_OUTOFMEMORY;
615 ret = URLCacheContainers_FindContainerW(url, ppContainer);
620 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
623 URLCACHECONTAINER * pContainer;
625 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
627 /* non-NULL search pattern only returns one container ever */
628 if (lpwszSearchPattern && dwIndex > 0)
631 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
633 if (lpwszSearchPattern)
635 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
637 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
638 *ppContainer = pContainer;
646 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
647 *ppContainer = pContainer;
656 /***********************************************************************
657 * URLCacheContainer_LockIndex (Internal)
659 * Locks the index for system-wide exclusive access.
662 * Cache file header if successful
663 * NULL if failed and calls SetLastError.
665 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
669 URLCACHE_HEADER * pHeader;
673 WaitForSingleObject(pContainer->hMutex, INFINITE);
675 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
679 ReleaseMutex(pContainer->hMutex);
680 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
683 pHeader = (URLCACHE_HEADER *)pIndexData;
685 /* file has grown - we need to remap to prevent us getting
686 * access violations when we try and access beyond the end
687 * of the memory mapped file */
688 if (pHeader->dwFileSize != pContainer->file_size)
690 UnmapViewOfFile( pHeader );
691 URLCacheContainer_CloseIndex(pContainer);
692 error = URLCacheContainer_OpenIndex(pContainer);
693 if (error != ERROR_SUCCESS)
695 ReleaseMutex(pContainer->hMutex);
699 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
703 ReleaseMutex(pContainer->hMutex);
704 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
707 pHeader = (URLCACHE_HEADER *)pIndexData;
710 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
712 for (index = 0; index < pHeader->DirectoryCount; index++)
714 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
720 /***********************************************************************
721 * URLCacheContainer_UnlockIndex (Internal)
724 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
727 ReleaseMutex(pContainer->hMutex);
728 return UnmapViewOfFile(pHeader);
733 #define CHAR_BIT (8 * sizeof(CHAR))
736 /***********************************************************************
737 * URLCache_Allocation_BlockIsFree (Internal)
739 * Is the specified block number free?
746 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
748 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
749 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
752 /***********************************************************************
753 * URLCache_Allocation_BlockFree (Internal)
755 * Marks the specified block as free
761 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
763 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
764 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
767 /***********************************************************************
768 * URLCache_Allocation_BlockAllocate (Internal)
770 * Marks the specified block as allocated
776 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
778 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
779 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
782 /***********************************************************************
783 * URLCache_FindFirstFreeEntry (Internal)
785 * Finds and allocates the first block of free space big enough and
786 * sets ppEntry to point to it.
789 * TRUE if it had enough space
790 * FALSE if it couldn't find enough space
793 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
795 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
798 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
800 for (dwFreeCounter = 0;
801 dwFreeCounter < dwBlocksNeeded &&
802 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
803 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
805 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
807 if (dwFreeCounter == dwBlocksNeeded)
810 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
811 for (index = 0; index < dwBlocksNeeded; index++)
812 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
813 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
814 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
815 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
816 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
820 FIXME("Grow file\n");
824 /***********************************************************************
825 * URLCache_DeleteEntry (Internal)
827 * Deletes the specified entry and frees the space allocated to it
830 * TRUE if it succeeded
834 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
838 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
840 /* update allocation table */
841 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
842 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
843 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
848 /***********************************************************************
849 * URLCache_LocalFileNameToPathW (Internal)
851 * Copies the full path to the specified buffer given the local file
852 * name and the index of the directory it is in. Always sets value in
853 * lpBufferSize to the required buffer size (in bytes).
856 * TRUE if the buffer was big enough
857 * FALSE if the buffer was too small
860 static BOOL URLCache_LocalFileNameToPathW(
861 const URLCACHECONTAINER * pContainer,
862 LPCURLCACHE_HEADER pHeader,
863 LPCSTR szLocalFileName,
869 int path_len = strlenW(pContainer->path);
870 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
871 if (Directory >= pHeader->DirectoryCount)
877 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
878 if (nRequired <= *lpBufferSize)
882 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
883 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
884 wszPath[dir_len + path_len] = '\\';
885 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
886 *lpBufferSize = nRequired;
889 *lpBufferSize = nRequired;
893 /***********************************************************************
894 * URLCache_LocalFileNameToPathA (Internal)
896 * Copies the full path to the specified buffer given the local file
897 * name and the index of the directory it is in. Always sets value in
898 * lpBufferSize to the required buffer size.
901 * TRUE if the buffer was big enough
902 * FALSE if the buffer was too small
905 static BOOL URLCache_LocalFileNameToPathA(
906 const URLCACHECONTAINER * pContainer,
907 LPCURLCACHE_HEADER pHeader,
908 LPCSTR szLocalFileName,
914 int path_len, file_name_len, dir_len;
916 if (Directory >= pHeader->DirectoryCount)
922 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
923 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
924 dir_len = DIR_LENGTH;
926 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
927 if (nRequired < *lpBufferSize)
929 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
930 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
931 szPath[path_len + dir_len] = '\\';
932 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
933 *lpBufferSize = nRequired;
936 *lpBufferSize = nRequired;
940 /* Just like DosDateTimeToFileTime, except that it also maps the special
941 * case of a DOS date/time of (0,0) to a filetime of (0,0).
943 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
946 if (!fatdate && !fattime)
947 ft->dwLowDateTime = ft->dwHighDateTime = 0;
949 DosDateTimeToFileTime(fatdate, fattime, ft);
952 /***********************************************************************
953 * URLCache_CopyEntry (Internal)
955 * Copies an entry from the cache index file to the Win32 structure
958 * ERROR_SUCCESS if the buffer was big enough
959 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
962 static DWORD URLCache_CopyEntry(
963 URLCACHECONTAINER * pContainer,
964 LPCURLCACHE_HEADER pHeader,
965 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
966 LPDWORD lpdwBufferSize,
967 const URL_CACHEFILE_ENTRY * pUrlEntry,
971 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
973 if (*lpdwBufferSize >= dwRequiredSize)
975 lpCacheEntryInfo->lpHeaderInfo = NULL;
976 lpCacheEntryInfo->lpszFileExtension = NULL;
977 lpCacheEntryInfo->lpszLocalFileName = NULL;
978 lpCacheEntryInfo->lpszSourceUrlName = NULL;
979 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
980 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
981 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
982 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
983 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
984 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
985 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
986 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
987 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
988 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
989 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
990 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
991 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
992 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
995 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
996 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
997 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
999 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1001 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1002 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1004 /* FIXME: is source url optional? */
1005 if (*lpdwBufferSize >= dwRequiredSize)
1007 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1009 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1011 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1013 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1016 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1017 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1018 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1020 if (pUrlEntry->dwOffsetLocalName)
1022 LONG nLocalFilePathSize;
1023 LPSTR lpszLocalFileName;
1024 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1025 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1026 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1027 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1029 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1031 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1033 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1034 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1035 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1037 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1039 if (*lpdwBufferSize >= dwRequiredSize)
1041 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1042 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1043 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1045 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1046 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1047 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1049 if (pUrlEntry->dwOffsetFileExtension)
1054 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1056 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1057 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1059 if (*lpdwBufferSize >= dwRequiredSize)
1061 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1063 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1065 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1068 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1069 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1070 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1073 if (dwRequiredSize > *lpdwBufferSize)
1075 *lpdwBufferSize = dwRequiredSize;
1076 return ERROR_INSUFFICIENT_BUFFER;
1078 *lpdwBufferSize = dwRequiredSize;
1079 return ERROR_SUCCESS;
1082 /* Just like FileTimeToDosDateTime, except that it also maps the special
1083 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1085 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1088 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1089 *fatdate = *fattime = 0;
1091 FileTimeToDosDateTime(ft, fatdate, fattime);
1094 /***********************************************************************
1095 * URLCache_SetEntryInfo (Internal)
1097 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1098 * according to the flags set by dwFieldControl.
1101 * ERROR_SUCCESS if the buffer was big enough
1102 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1105 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1107 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1108 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1109 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1110 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1111 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1112 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1113 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1114 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1115 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1116 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1117 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1118 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1119 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1120 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1121 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1122 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1124 return ERROR_SUCCESS;
1127 /***********************************************************************
1128 * URLCache_HashKey (Internal)
1130 * Returns the hash key for a given string
1133 * hash key for the string
1136 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1138 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1139 * but the algorithm and result are not the same!
1141 static const unsigned char lookupTable[256] =
1143 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1144 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1145 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1146 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1147 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1148 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1149 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1150 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1151 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1152 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1153 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1154 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1155 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1156 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1157 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1158 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1159 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1160 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1161 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1162 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1163 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1164 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1165 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1166 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1167 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1168 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1169 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1170 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1171 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1172 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1173 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1174 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1179 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1180 key[i] = lookupTable[i];
1182 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1184 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1185 key[i] = lookupTable[*lpszKey ^ key[i]];
1188 return *(DWORD *)key;
1191 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1193 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1196 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1198 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1199 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1200 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1203 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1205 /* structure of hash table:
1206 * 448 entries divided into 64 blocks
1207 * each block therefore contains a chain of 7 key/offset pairs
1208 * how position in table is calculated:
1209 * 1. the url is hashed in helper function
1210 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1211 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1214 * there can be multiple hash tables in the file and the offset to
1215 * the next one is stored in the header of the hash table
1217 DWORD key = URLCache_HashKey(lpszUrl);
1218 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1219 HASH_CACHEFILE_ENTRY * pHashEntry;
1220 DWORD dwHashTableNumber = 0;
1222 key >>= HASHTABLE_FLAG_BITS;
1224 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1225 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1226 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1229 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1231 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1234 /* make sure that it is in fact a hash entry */
1235 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1237 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1241 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1243 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1244 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1246 /* FIXME: we should make sure that this is the right element
1247 * before returning and claiming that it is. We can do this
1248 * by doing a simple compare between the URL we were given
1249 * and the URL stored in the entry. However, this assumes
1250 * we know the format of all the entries stored in the
1252 *ppHashEntry = pHashElement;
1260 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1265 urlA = heap_strdupWtoA(lpszUrl);
1268 SetLastError(ERROR_OUTOFMEMORY);
1272 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1277 /***********************************************************************
1278 * URLCache_HashEntrySetFlags (Internal)
1280 * Sets special bits in hash key
1286 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1288 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1291 /***********************************************************************
1292 * URLCache_DeleteEntryFromHash (Internal)
1294 * Searches all the hash tables in the index for the given URL and
1295 * then if found deletes the entry.
1298 * TRUE if the entry was found
1299 * FALSE if the entry could not be found
1302 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1304 pHashEntry->dwHashKey = HASHTABLE_DEL;
1308 /***********************************************************************
1309 * URLCache_AddEntryToHash (Internal)
1311 * Searches all the hash tables for a free slot based on the offset
1312 * generated from the hash key. If a free slot is found, the offset and
1313 * key are entered into the hash table.
1316 * ERROR_SUCCESS if the entry was added
1317 * Any other Win32 error code if the entry could not be added
1320 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1322 /* see URLCache_FindEntryInHash for structure of hash tables */
1324 DWORD key = URLCache_HashKey(lpszUrl);
1325 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1326 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1327 DWORD dwHashTableNumber = 0;
1330 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1332 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1333 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1334 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1337 pHashPrev = pHashEntry;
1339 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1341 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1344 /* make sure that it is in fact a hash entry */
1345 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1347 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1351 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1353 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1354 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1356 pHashElement->dwHashKey = key;
1357 pHashElement->dwOffsetEntry = dwOffsetEntry;
1358 return ERROR_SUCCESS;
1362 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1363 if (error != ERROR_SUCCESS)
1366 pHashEntry->HashTable[offset].dwHashKey = key;
1367 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1368 return ERROR_SUCCESS;
1371 /***********************************************************************
1372 * URLCache_CreateHashTable (Internal)
1374 * Creates a new hash table in free space and adds it to the chain of existing
1378 * ERROR_SUCCESS if the hash table was created
1379 * ERROR_DISK_FULL if the hash table could not be created
1382 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1387 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1389 FIXME("no free space for hash table\n");
1390 return ERROR_DISK_FULL;
1393 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1396 pPrevHash->dwAddressNext = dwOffset;
1398 pHeader->dwOffsetFirstHashTable = dwOffset;
1399 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1400 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1401 (*ppHash)->dwAddressNext = 0;
1402 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1403 for (i = 0; i < HASHTABLE_SIZE; i++)
1405 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1406 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1408 return ERROR_SUCCESS;
1411 /***********************************************************************
1412 * URLCache_EnumHashTables (Internal)
1414 * Enumerates the hash tables in a container.
1417 * TRUE if an entry was found
1418 * FALSE if there are no more tables to enumerate.
1421 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1423 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1424 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1425 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1427 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1428 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1430 /* make sure that it is in fact a hash entry */
1431 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1433 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1434 (*pdwHashTableNumber)++;
1438 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1444 /***********************************************************************
1445 * URLCache_EnumHashTableEntries (Internal)
1447 * Enumerates entries in a hash table and returns the next non-free entry.
1450 * TRUE if an entry was found
1451 * FALSE if the hash table is empty or there are no more entries to
1455 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1456 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1458 for (; *index < HASHTABLE_SIZE ; (*index)++)
1460 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1463 *ppHashEntry = &pHashEntry->HashTable[*index];
1464 TRACE("entry found %d\n", *index);
1467 TRACE("no more entries (%d)\n", *index);
1471 /***********************************************************************
1472 * URLCache_DeleteCacheDirectory (Internal)
1474 * Erase a directory containing an URL cache.
1477 * TRUE success, FALSE failure/aborted.
1480 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1483 WCHAR path[MAX_PATH + 1];
1484 SHFILEOPSTRUCTW shfos;
1487 path_len = strlenW(lpszPath);
1488 if (path_len >= MAX_PATH)
1490 strcpyW(path, lpszPath);
1491 path[path_len + 1] = 0; /* double-NUL-terminate path */
1494 shfos.wFunc = FO_DELETE;
1498 shfos.fAnyOperationsAborted = FALSE;
1499 ret = SHFileOperationW(&shfos);
1501 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1502 return !(ret || shfos.fAnyOperationsAborted);
1505 /***********************************************************************
1506 * FreeUrlCacheSpaceW (WININET.@)
1508 * Frees up some cache.
1511 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1512 * dwSize [I] How much space to free up.
1513 * dwSizeType [I] How to interpret dwSize.
1516 * TRUE success. FALSE failure.
1519 * This implementation just retrieves the path of the cache directory, and
1520 * deletes its contents from the filesystem. The correct approach would
1521 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1523 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1525 URLCACHECONTAINER * pContainer;
1527 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1529 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1530 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1534 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1536 /* The URL cache has prefix L"" (unlike Cookies and History) */
1537 if (pContainer->cache_prefix[0] == 0)
1541 WaitForSingleObject(pContainer->hMutex, INFINITE);
1543 /* unlock, delete, recreate and lock cache */
1544 URLCacheContainer_CloseIndex(pContainer);
1545 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1546 ret_open = URLCacheContainer_OpenIndex(pContainer);
1548 ReleaseMutex(pContainer->hMutex);
1549 return ret_del && (ret_open == ERROR_SUCCESS);
1555 /***********************************************************************
1556 * FreeUrlCacheSpaceA (WININET.@)
1558 * See FreeUrlCacheSpaceW.
1560 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1563 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1564 if (lpszCachePath == NULL || path != NULL)
1565 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1570 /***********************************************************************
1571 * GetUrlCacheEntryInfoExA (WININET.@)
1574 BOOL WINAPI GetUrlCacheEntryInfoExA(
1576 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1577 LPDWORD lpdwCacheEntryInfoBufSize,
1579 LPDWORD lpdwReserved,
1583 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1584 debugstr_a(lpszUrl),
1586 lpdwCacheEntryInfoBufSize,
1592 if ((lpszReserved != NULL) ||
1593 (lpdwReserved != NULL) ||
1594 (lpReserved != NULL))
1596 ERR("Reserved value was not 0\n");
1597 SetLastError(ERROR_INVALID_PARAMETER);
1602 FIXME("Undocumented flag(s): %x\n", dwFlags);
1603 SetLastError(ERROR_FILE_NOT_FOUND);
1606 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1609 /***********************************************************************
1610 * GetUrlCacheEntryInfoA (WININET.@)
1613 BOOL WINAPI GetUrlCacheEntryInfoA(
1614 IN LPCSTR lpszUrlName,
1615 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1616 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1619 LPURLCACHE_HEADER pHeader;
1620 struct _HASH_ENTRY * pHashEntry;
1621 const CACHEFILE_ENTRY * pEntry;
1622 const URL_CACHEFILE_ENTRY * pUrlEntry;
1623 URLCACHECONTAINER * pContainer;
1626 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1628 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1629 if (error != ERROR_SUCCESS)
1631 SetLastError(error);
1635 error = URLCacheContainer_OpenIndex(pContainer);
1636 if (error != ERROR_SUCCESS)
1638 SetLastError(error);
1642 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1645 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1647 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1648 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1649 SetLastError(ERROR_FILE_NOT_FOUND);
1653 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1654 if (pEntry->dwSignature != URL_SIGNATURE)
1656 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1657 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1658 SetLastError(ERROR_FILE_NOT_FOUND);
1662 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1663 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1664 if (pUrlEntry->dwOffsetHeaderInfo)
1665 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1667 if (lpdwCacheEntryInfoBufferSize)
1669 if (!lpCacheEntryInfo)
1670 *lpdwCacheEntryInfoBufferSize = 0;
1672 error = URLCache_CopyEntry(
1676 lpdwCacheEntryInfoBufferSize,
1679 if (error != ERROR_SUCCESS)
1681 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1682 SetLastError(error);
1685 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1688 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1693 /***********************************************************************
1694 * GetUrlCacheEntryInfoW (WININET.@)
1697 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1698 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1699 LPDWORD lpdwCacheEntryInfoBufferSize)
1701 LPURLCACHE_HEADER pHeader;
1702 struct _HASH_ENTRY * pHashEntry;
1703 const CACHEFILE_ENTRY * pEntry;
1704 const URL_CACHEFILE_ENTRY * pUrlEntry;
1705 URLCACHECONTAINER * pContainer;
1708 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1710 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1711 if (error != ERROR_SUCCESS)
1713 SetLastError(error);
1717 error = URLCacheContainer_OpenIndex(pContainer);
1718 if (error != ERROR_SUCCESS)
1720 SetLastError(error);
1724 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1727 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1729 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1730 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1731 SetLastError(ERROR_FILE_NOT_FOUND);
1735 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1736 if (pEntry->dwSignature != URL_SIGNATURE)
1738 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1739 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1740 SetLastError(ERROR_FILE_NOT_FOUND);
1744 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1745 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1746 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1748 if (lpdwCacheEntryInfoBufferSize)
1750 if (!lpCacheEntryInfo)
1751 *lpdwCacheEntryInfoBufferSize = 0;
1753 error = URLCache_CopyEntry(
1756 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1757 lpdwCacheEntryInfoBufferSize,
1759 TRUE /* UNICODE */);
1760 if (error != ERROR_SUCCESS)
1762 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1763 SetLastError(error);
1766 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1769 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1774 /***********************************************************************
1775 * GetUrlCacheEntryInfoExW (WININET.@)
1778 BOOL WINAPI GetUrlCacheEntryInfoExW(
1780 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1781 LPDWORD lpdwCacheEntryInfoBufSize,
1782 LPWSTR lpszReserved,
1783 LPDWORD lpdwReserved,
1787 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1788 debugstr_w(lpszUrl),
1790 lpdwCacheEntryInfoBufSize,
1796 if ((lpszReserved != NULL) ||
1797 (lpdwReserved != NULL) ||
1798 (lpReserved != NULL))
1800 ERR("Reserved value was not 0\n");
1801 SetLastError(ERROR_INVALID_PARAMETER);
1806 FIXME("Undocumented flag(s): %x\n", dwFlags);
1807 SetLastError(ERROR_FILE_NOT_FOUND);
1810 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1813 /***********************************************************************
1814 * SetUrlCacheEntryInfoA (WININET.@)
1816 BOOL WINAPI SetUrlCacheEntryInfoA(
1818 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1819 DWORD dwFieldControl)
1821 LPURLCACHE_HEADER pHeader;
1822 struct _HASH_ENTRY * pHashEntry;
1823 CACHEFILE_ENTRY * pEntry;
1824 URLCACHECONTAINER * pContainer;
1827 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1829 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1830 if (error != ERROR_SUCCESS)
1832 SetLastError(error);
1836 error = URLCacheContainer_OpenIndex(pContainer);
1837 if (error != ERROR_SUCCESS)
1839 SetLastError(error);
1843 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1846 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1848 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1849 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1850 SetLastError(ERROR_FILE_NOT_FOUND);
1854 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1855 if (pEntry->dwSignature != URL_SIGNATURE)
1857 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1858 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1859 SetLastError(ERROR_FILE_NOT_FOUND);
1863 URLCache_SetEntryInfo(
1864 (URL_CACHEFILE_ENTRY *)pEntry,
1865 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1868 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1873 /***********************************************************************
1874 * SetUrlCacheEntryInfoW (WININET.@)
1876 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1878 LPURLCACHE_HEADER pHeader;
1879 struct _HASH_ENTRY * pHashEntry;
1880 CACHEFILE_ENTRY * pEntry;
1881 URLCACHECONTAINER * pContainer;
1884 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1886 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1887 if (error != ERROR_SUCCESS)
1889 SetLastError(error);
1893 error = URLCacheContainer_OpenIndex(pContainer);
1894 if (error != ERROR_SUCCESS)
1896 SetLastError(error);
1900 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1903 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1905 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1906 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1907 SetLastError(ERROR_FILE_NOT_FOUND);
1911 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1912 if (pEntry->dwSignature != URL_SIGNATURE)
1914 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1915 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1916 SetLastError(ERROR_FILE_NOT_FOUND);
1920 URLCache_SetEntryInfo(
1921 (URL_CACHEFILE_ENTRY *)pEntry,
1925 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1930 /***********************************************************************
1931 * RetrieveUrlCacheEntryFileA (WININET.@)
1934 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1935 IN LPCSTR lpszUrlName,
1936 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1937 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1941 LPURLCACHE_HEADER pHeader;
1942 struct _HASH_ENTRY * pHashEntry;
1943 CACHEFILE_ENTRY * pEntry;
1944 URL_CACHEFILE_ENTRY * pUrlEntry;
1945 URLCACHECONTAINER * pContainer;
1948 TRACE("(%s, %p, %p, 0x%08x)\n",
1949 debugstr_a(lpszUrlName),
1951 lpdwCacheEntryInfoBufferSize,
1954 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1955 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1957 SetLastError(ERROR_INVALID_PARAMETER);
1961 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1962 if (error != ERROR_SUCCESS)
1964 SetLastError(error);
1968 error = URLCacheContainer_OpenIndex(pContainer);
1969 if (error != ERROR_SUCCESS)
1971 SetLastError(error);
1975 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1978 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1980 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1981 TRACE("entry %s not found!\n", lpszUrlName);
1982 SetLastError(ERROR_FILE_NOT_FOUND);
1986 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1987 if (pEntry->dwSignature != URL_SIGNATURE)
1989 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1990 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1991 SetLastError(ERROR_FILE_NOT_FOUND);
1995 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1996 if (!pUrlEntry->dwOffsetLocalName)
1998 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1999 SetLastError(ERROR_INVALID_DATA);
2003 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2004 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2006 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2007 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2009 if (error != ERROR_SUCCESS)
2011 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2012 SetLastError(error);
2015 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2017 pUrlEntry->dwHitRate++;
2018 pUrlEntry->dwUseCount++;
2019 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2020 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2022 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2027 /***********************************************************************
2028 * RetrieveUrlCacheEntryFileW (WININET.@)
2031 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2032 IN LPCWSTR lpszUrlName,
2033 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2034 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2038 LPURLCACHE_HEADER pHeader;
2039 struct _HASH_ENTRY * pHashEntry;
2040 CACHEFILE_ENTRY * pEntry;
2041 URL_CACHEFILE_ENTRY * pUrlEntry;
2042 URLCACHECONTAINER * pContainer;
2045 TRACE("(%s, %p, %p, 0x%08x)\n",
2046 debugstr_w(lpszUrlName),
2048 lpdwCacheEntryInfoBufferSize,
2051 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2052 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2054 SetLastError(ERROR_INVALID_PARAMETER);
2058 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2059 if (error != ERROR_SUCCESS)
2061 SetLastError(error);
2065 error = URLCacheContainer_OpenIndex(pContainer);
2066 if (error != ERROR_SUCCESS)
2068 SetLastError(error);
2072 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2075 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2077 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2078 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2079 SetLastError(ERROR_FILE_NOT_FOUND);
2083 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2084 if (pEntry->dwSignature != URL_SIGNATURE)
2086 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2087 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2088 SetLastError(ERROR_FILE_NOT_FOUND);
2092 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2093 if (!pUrlEntry->dwOffsetLocalName)
2095 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2096 SetLastError(ERROR_INVALID_DATA);
2100 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2101 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2103 error = URLCache_CopyEntry(
2106 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2107 lpdwCacheEntryInfoBufferSize,
2109 TRUE /* UNICODE */);
2110 if (error != ERROR_SUCCESS)
2112 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2113 SetLastError(error);
2116 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2118 pUrlEntry->dwHitRate++;
2119 pUrlEntry->dwUseCount++;
2120 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2121 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2123 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2128 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2129 struct _HASH_ENTRY *pHashEntry)
2131 CACHEFILE_ENTRY * pEntry;
2132 URL_CACHEFILE_ENTRY * pUrlEntry;
2134 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2135 if (pEntry->dwSignature != URL_SIGNATURE)
2137 FIXME("Trying to delete entry of unknown format %s\n",
2138 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2139 SetLastError(ERROR_FILE_NOT_FOUND);
2143 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2144 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2146 /* FIXME: implement timeout object unlocking */
2147 TRACE("Trying to delete locked entry\n");
2148 pUrlEntry->CacheEntryType |= DELETED_CACHE_ENTRY;
2149 SetLastError(ERROR_SHARING_VIOLATION);
2153 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2155 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2156 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2158 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2160 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2161 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2163 pHeader->ExemptUsage.QuadPart = 0;
2167 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2168 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2170 pHeader->CacheUsage.QuadPart = 0;
2173 URLCache_DeleteEntry(pHeader, pEntry);
2175 URLCache_DeleteEntryFromHash(pHashEntry);
2179 /***********************************************************************
2180 * UnlockUrlCacheEntryFileA (WININET.@)
2183 BOOL WINAPI UnlockUrlCacheEntryFileA(
2184 IN LPCSTR lpszUrlName,
2188 LPURLCACHE_HEADER pHeader;
2189 struct _HASH_ENTRY * pHashEntry;
2190 CACHEFILE_ENTRY * pEntry;
2191 URL_CACHEFILE_ENTRY * pUrlEntry;
2192 URLCACHECONTAINER * pContainer;
2195 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2199 ERR("dwReserved != 0\n");
2200 SetLastError(ERROR_INVALID_PARAMETER);
2204 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2205 if (error != ERROR_SUCCESS)
2207 SetLastError(error);
2211 error = URLCacheContainer_OpenIndex(pContainer);
2212 if (error != ERROR_SUCCESS)
2214 SetLastError(error);
2218 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2221 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2223 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2224 TRACE("entry %s not found!\n", lpszUrlName);
2225 SetLastError(ERROR_FILE_NOT_FOUND);
2229 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2230 if (pEntry->dwSignature != URL_SIGNATURE)
2232 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2233 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2234 SetLastError(ERROR_FILE_NOT_FOUND);
2238 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2240 if (pUrlEntry->dwUseCount == 0)
2242 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2245 pUrlEntry->dwUseCount--;
2246 if (!pUrlEntry->dwUseCount)
2248 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2249 if (pUrlEntry->CacheEntryType & DELETED_CACHE_ENTRY)
2250 DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
2253 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2258 /***********************************************************************
2259 * UnlockUrlCacheEntryFileW (WININET.@)
2262 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2264 LPURLCACHE_HEADER pHeader;
2265 struct _HASH_ENTRY * pHashEntry;
2266 CACHEFILE_ENTRY * pEntry;
2267 URL_CACHEFILE_ENTRY * pUrlEntry;
2268 URLCACHECONTAINER * pContainer;
2271 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2275 ERR("dwReserved != 0\n");
2276 SetLastError(ERROR_INVALID_PARAMETER);
2280 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2281 if (error != ERROR_SUCCESS)
2283 SetLastError(error);
2287 error = URLCacheContainer_OpenIndex(pContainer);
2288 if (error != ERROR_SUCCESS)
2290 SetLastError(error);
2294 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2297 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2299 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2300 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2301 SetLastError(ERROR_FILE_NOT_FOUND);
2305 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2306 if (pEntry->dwSignature != URL_SIGNATURE)
2308 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2309 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2310 SetLastError(ERROR_FILE_NOT_FOUND);
2314 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2316 if (pUrlEntry->dwUseCount == 0)
2318 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2321 pUrlEntry->dwUseCount--;
2322 if (!pUrlEntry->dwUseCount)
2323 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2325 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2330 /***********************************************************************
2331 * CreateUrlCacheEntryA (WININET.@)
2334 BOOL WINAPI CreateUrlCacheEntryA(
2335 IN LPCSTR lpszUrlName,
2336 IN DWORD dwExpectedFileSize,
2337 IN LPCSTR lpszFileExtension,
2338 OUT LPSTR lpszFileName,
2343 WCHAR *file_extension = NULL;
2344 WCHAR file_name[MAX_PATH];
2345 BOOL bSuccess = FALSE;
2348 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2349 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2351 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2353 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2355 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2357 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2363 dwError = GetLastError();
2368 dwError = GetLastError();
2370 heap_free(file_extension);
2374 dwError = GetLastError();
2376 heap_free(url_name);
2377 if (!bSuccess) SetLastError(dwError);
2381 /***********************************************************************
2382 * CreateUrlCacheEntryW (WININET.@)
2385 BOOL WINAPI CreateUrlCacheEntryW(
2386 IN LPCWSTR lpszUrlName,
2387 IN DWORD dwExpectedFileSize,
2388 IN LPCWSTR lpszFileExtension,
2389 OUT LPWSTR lpszFileName,
2393 URLCACHECONTAINER * pContainer;
2394 LPURLCACHE_HEADER pHeader;
2395 CHAR szFile[MAX_PATH];
2396 WCHAR szExtension[MAX_PATH];
2397 LPCWSTR lpszUrlPart;
2399 LPCWSTR lpszFileNameExtension;
2400 LPWSTR lpszFileNameNoPath;
2402 int countnoextension;
2405 BOOL bFound = FALSE;
2411 static const WCHAR szWWW[] = {'w','w','w',0};
2412 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2414 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2415 debugstr_w(lpszUrlName),
2417 debugstr_w(lpszFileExtension),
2422 FIXME("dwReserved 0x%08x\n", dwReserved);
2424 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2426 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2429 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2431 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2433 lpszUrlEnd = lpszUrlPart;
2435 for (lpszUrlPart = lpszUrlEnd;
2436 (lpszUrlPart >= lpszUrlName);
2439 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2446 if (!lstrcmpW(lpszUrlPart, szWWW))
2448 lpszUrlPart += lstrlenW(szWWW);
2451 count = lpszUrlEnd - lpszUrlPart;
2453 if (bFound && (count < MAX_PATH))
2455 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2459 while(len && szFile[--len] == '/') szFile[len] = '\0';
2461 /* FIXME: get rid of illegal characters like \, / and : */
2465 FIXME("need to generate a random filename\n");
2468 TRACE("File name: %s\n", debugstr_a(szFile));
2470 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2471 if (error != ERROR_SUCCESS)
2473 SetLastError(error);
2477 error = URLCacheContainer_OpenIndex(pContainer);
2478 if (error != ERROR_SUCCESS)
2480 SetLastError(error);
2484 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2487 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2489 lBufferSize = MAX_PATH * sizeof(WCHAR);
2490 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2492 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2493 debugstr_a(szFile), lBufferSize);
2494 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2498 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2500 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2501 lpszFileNameNoPath >= lpszFileName;
2502 --lpszFileNameNoPath)
2504 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2508 countnoextension = lstrlenW(lpszFileNameNoPath);
2509 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2510 if (lpszFileNameExtension)
2511 countnoextension -= lstrlenW(lpszFileNameExtension);
2512 *szExtension = '\0';
2514 if (lpszFileExtension)
2516 szExtension[0] = '.';
2517 lstrcpyW(szExtension+1, lpszFileExtension);
2520 for (i = 0; i < 255; i++)
2522 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2525 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2526 for (p = lpszFileNameNoPath + 1; *p; p++)
2532 case '/': case '\\':
2539 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2541 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2542 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2543 if (hFile != INVALID_HANDLE_VALUE)
2550 GetSystemTimeAsFileTime(&ft);
2551 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2553 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2554 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2555 if (hFile != INVALID_HANDLE_VALUE)
2561 WARN("Could not find a unique filename\n");
2566 /***********************************************************************
2567 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2569 * The bug we are compensating for is that some drongo at Microsoft
2570 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2571 * As a consequence, CommitUrlCacheEntryA has been effectively
2572 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2573 * is still defined as LPCWSTR. The result (other than madness) is
2574 * that we always need to store lpHeaderInfo in CP_ACP rather than
2575 * in UTF16, and we need to avoid converting lpHeaderInfo in
2576 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2577 * result will lose data for arbitrary binary data.
2580 static BOOL CommitUrlCacheEntryInternal(
2581 IN LPCWSTR lpszUrlName,
2582 IN LPCWSTR lpszLocalFileName,
2583 IN FILETIME ExpireTime,
2584 IN FILETIME LastModifiedTime,
2585 IN DWORD CacheEntryType,
2586 IN LPBYTE lpHeaderInfo,
2587 IN DWORD dwHeaderSize,
2588 IN LPCWSTR lpszFileExtension,
2589 IN LPCWSTR lpszOriginalUrl
2592 URLCACHECONTAINER * pContainer;
2593 LPURLCACHE_HEADER pHeader;
2594 struct _HASH_ENTRY * pHashEntry;
2595 CACHEFILE_ENTRY * pEntry;
2596 URL_CACHEFILE_ENTRY * pUrlEntry;
2597 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2598 DWORD dwOffsetLocalFileName = 0;
2599 DWORD dwOffsetHeader = 0;
2600 DWORD dwOffsetFileExtension = 0;
2601 LARGE_INTEGER file_size;
2602 BYTE cDirectory = 0;
2603 char achFile[MAX_PATH];
2604 LPSTR lpszUrlNameA = NULL;
2605 LPSTR lpszFileExtensionA = NULL;
2606 char *pchLocalFileName = 0;
2609 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2610 debugstr_w(lpszUrlName),
2611 debugstr_w(lpszLocalFileName),
2615 debugstr_w(lpszFileExtension),
2616 debugstr_w(lpszOriginalUrl));
2618 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2620 SetLastError(ERROR_INVALID_PARAMETER);
2623 if (lpszOriginalUrl)
2624 WARN(": lpszOriginalUrl ignored\n");
2626 file_size.QuadPart = 0;
2627 if (lpszLocalFileName)
2631 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2632 if (hFile == INVALID_HANDLE_VALUE)
2634 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2639 if (!GetFileSizeEx(hFile, &file_size))
2641 ERR("couldn't get file size (error is %d)\n", GetLastError());
2649 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2650 if (error != ERROR_SUCCESS)
2652 SetLastError(error);
2656 error = URLCacheContainer_OpenIndex(pContainer);
2657 if (error != ERROR_SUCCESS)
2659 SetLastError(error);
2663 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2666 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2669 error = GetLastError();
2673 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2675 error = GetLastError();
2679 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2681 FIXME("entry already in cache - don't know what to do!\n");
2683 * SetLastError(ERROR_FILE_NOT_FOUND);
2689 if (lpszLocalFileName)
2691 BOOL bFound = FALSE;
2693 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2695 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2696 error = ERROR_INVALID_PARAMETER;
2700 /* skip container path prefix */
2701 lpszLocalFileName += lstrlenW(pContainer->path);
2703 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2704 pchLocalFileName = achFile;
2706 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2708 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2717 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2718 error = ERROR_INVALID_PARAMETER;
2722 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2725 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2726 if (lpszLocalFileName)
2728 dwOffsetLocalFileName = dwBytesNeeded;
2729 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2733 dwOffsetHeader = dwBytesNeeded;
2734 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2736 if (lpszFileExtensionA)
2738 dwOffsetFileExtension = dwBytesNeeded;
2739 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2742 /* round up to next block */
2743 if (dwBytesNeeded % BLOCKSIZE)
2745 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2746 dwBytesNeeded += BLOCKSIZE;
2749 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2751 ERR("no free entries\n");
2752 error = ERROR_DISK_FULL;
2756 /* FindFirstFreeEntry fills in blocks used */
2757 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2758 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2759 pUrlEntry->CacheDir = cDirectory;
2760 pUrlEntry->CacheEntryType = CacheEntryType;
2761 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2762 if (CacheEntryType & STICKY_CACHE_ENTRY)
2764 /* Sticky entries have a default exempt time of one day */
2765 pUrlEntry->dwExemptDelta = 86400;
2768 pUrlEntry->dwExemptDelta = 0;
2769 pUrlEntry->dwHitRate = 0;
2770 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2771 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2772 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2773 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2774 pUrlEntry->size.QuadPart = file_size.QuadPart;
2775 pUrlEntry->dwUseCount = 0;
2776 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2777 pUrlEntry->LastModifiedTime = LastModifiedTime;
2778 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2779 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2780 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2781 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2784 pUrlEntry->dwUnknown1 = 0;
2785 pUrlEntry->dwUnknown2 = 0;
2786 pUrlEntry->dwUnknown3 = 0x60;
2787 pUrlEntry->Unknown4 = 0;
2788 pUrlEntry->wUnknown5 = 0x1010;
2789 pUrlEntry->dwUnknown7 = 0;
2790 pUrlEntry->dwUnknown8 = 0;
2793 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2794 if (dwOffsetLocalFileName)
2795 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2797 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2798 if (dwOffsetFileExtension)
2799 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2801 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2802 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader), HASHTABLE_URL);
2803 if (error != ERROR_SUCCESS)
2804 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2807 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2808 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2809 if (CacheEntryType & STICKY_CACHE_ENTRY)
2810 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2812 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2813 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2814 pHeader->CacheLimit.QuadPart)
2815 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2819 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2820 heap_free(lpszUrlNameA);
2821 heap_free(lpszFileExtensionA);
2823 if (error == ERROR_SUCCESS)
2827 SetLastError(error);
2832 /***********************************************************************
2833 * CommitUrlCacheEntryA (WININET.@)
2836 BOOL WINAPI CommitUrlCacheEntryA(
2837 IN LPCSTR lpszUrlName,
2838 IN LPCSTR lpszLocalFileName,
2839 IN FILETIME ExpireTime,
2840 IN FILETIME LastModifiedTime,
2841 IN DWORD CacheEntryType,
2842 IN LPBYTE lpHeaderInfo,
2843 IN DWORD dwHeaderSize,
2844 IN LPCSTR lpszFileExtension,
2845 IN LPCSTR lpszOriginalUrl
2848 WCHAR *url_name = NULL;
2849 WCHAR *local_file_name = NULL;
2850 WCHAR *original_url = NULL;
2851 WCHAR *file_extension = NULL;
2852 BOOL bSuccess = FALSE;
2854 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2855 debugstr_a(lpszUrlName),
2856 debugstr_a(lpszLocalFileName),
2860 debugstr_a(lpszFileExtension),
2861 debugstr_a(lpszOriginalUrl));
2863 url_name = heap_strdupAtoW(lpszUrlName);
2867 if (lpszLocalFileName)
2869 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2870 if (!local_file_name)
2873 if (lpszFileExtension)
2875 file_extension = heap_strdupAtoW(lpszFileExtension);
2876 if (!file_extension)
2879 if (lpszOriginalUrl)
2881 original_url = heap_strdupAtoW(lpszOriginalUrl);
2886 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2887 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2888 file_extension, original_url);
2891 heap_free(original_url);
2892 heap_free(file_extension);
2893 heap_free(local_file_name);
2894 heap_free(url_name);
2898 /***********************************************************************
2899 * CommitUrlCacheEntryW (WININET.@)
2902 BOOL WINAPI CommitUrlCacheEntryW(
2903 IN LPCWSTR lpszUrlName,
2904 IN LPCWSTR lpszLocalFileName,
2905 IN FILETIME ExpireTime,
2906 IN FILETIME LastModifiedTime,
2907 IN DWORD CacheEntryType,
2908 IN LPWSTR lpHeaderInfo,
2909 IN DWORD dwHeaderSize,
2910 IN LPCWSTR lpszFileExtension,
2911 IN LPCWSTR lpszOriginalUrl
2915 BOOL bSuccess = FALSE;
2917 CHAR *header_info = NULL;
2919 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2920 debugstr_w(lpszUrlName),
2921 debugstr_w(lpszLocalFileName),
2925 debugstr_w(lpszFileExtension),
2926 debugstr_w(lpszOriginalUrl));
2928 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2930 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2931 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2937 dwError = GetLastError();
2941 heap_free(header_info);
2943 SetLastError(dwError);
2949 /***********************************************************************
2950 * ReadUrlCacheEntryStream (WININET.@)
2953 BOOL WINAPI ReadUrlCacheEntryStream(
2954 IN HANDLE hUrlCacheStream,
2955 IN DWORD dwLocation,
2956 IN OUT LPVOID lpBuffer,
2957 IN OUT LPDWORD lpdwLen,
2961 /* Get handle to file from 'stream' */
2962 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2964 if (dwReserved != 0)
2966 ERR("dwReserved != 0\n");
2967 SetLastError(ERROR_INVALID_PARAMETER);
2971 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2973 SetLastError(ERROR_INVALID_HANDLE);
2977 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2979 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2982 /***********************************************************************
2983 * RetrieveUrlCacheEntryStreamA (WININET.@)
2986 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2987 IN LPCSTR lpszUrlName,
2988 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2989 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2990 IN BOOL fRandomRead,
2994 /* NOTE: this is not the same as the way that the native
2995 * version allocates 'stream' handles. I did it this way
2996 * as it is much easier and no applications should depend
2997 * on this behaviour. (Native version appears to allocate
2998 * indices into a table)
3000 STREAM_HANDLE * pStream;
3003 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3004 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3006 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3008 lpdwCacheEntryInfoBufferSize,
3014 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3019 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3021 if (hFile == INVALID_HANDLE_VALUE)
3024 /* allocate handle storage space */
3025 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3029 SetLastError(ERROR_OUTOFMEMORY);
3033 pStream->hFile = hFile;
3034 strcpy(pStream->lpszUrl, lpszUrlName);
3038 /***********************************************************************
3039 * RetrieveUrlCacheEntryStreamW (WININET.@)
3042 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3043 IN LPCWSTR lpszUrlName,
3044 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3045 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3046 IN BOOL fRandomRead,
3052 /* NOTE: this is not the same as the way that the native
3053 * version allocates 'stream' handles. I did it this way
3054 * as it is much easier and no applications should depend
3055 * on this behaviour. (Native version appears to allocate
3056 * indices into a table)
3058 STREAM_HANDLE * pStream;
3061 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3062 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3064 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3066 lpdwCacheEntryInfoBufferSize,
3072 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3077 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3079 if (hFile == INVALID_HANDLE_VALUE)
3082 /* allocate handle storage space */
3083 size = sizeof(STREAM_HANDLE);
3084 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3086 pStream = heap_alloc(size);
3090 SetLastError(ERROR_OUTOFMEMORY);
3094 pStream->hFile = hFile;
3095 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3099 /***********************************************************************
3100 * UnlockUrlCacheEntryStream (WININET.@)
3103 BOOL WINAPI UnlockUrlCacheEntryStream(
3104 IN HANDLE hUrlCacheStream,
3108 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3110 if (dwReserved != 0)
3112 ERR("dwReserved != 0\n");
3113 SetLastError(ERROR_INVALID_PARAMETER);
3117 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3119 SetLastError(ERROR_INVALID_HANDLE);
3123 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3126 CloseHandle(pStream->hFile);
3132 /***********************************************************************
3133 * DeleteUrlCacheEntryA (WININET.@)
3136 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3138 URLCACHECONTAINER * pContainer;
3139 LPURLCACHE_HEADER pHeader;
3140 struct _HASH_ENTRY * pHashEntry;
3144 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3146 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3147 if (error != ERROR_SUCCESS)
3149 SetLastError(error);
3153 error = URLCacheContainer_OpenIndex(pContainer);
3154 if (error != ERROR_SUCCESS)
3156 SetLastError(error);
3160 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3163 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3165 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3166 TRACE("entry %s not found!\n", lpszUrlName);
3167 SetLastError(ERROR_FILE_NOT_FOUND);
3171 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3173 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3178 /***********************************************************************
3179 * DeleteUrlCacheEntryW (WININET.@)
3182 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3184 URLCACHECONTAINER * pContainer;
3185 LPURLCACHE_HEADER pHeader;
3186 struct _HASH_ENTRY * pHashEntry;
3191 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3193 urlA = heap_strdupWtoA(lpszUrlName);
3196 SetLastError(ERROR_OUTOFMEMORY);
3200 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3201 if (error != ERROR_SUCCESS)
3204 SetLastError(error);
3208 error = URLCacheContainer_OpenIndex(pContainer);
3209 if (error != ERROR_SUCCESS)
3212 SetLastError(error);
3216 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3222 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3224 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3225 TRACE("entry %s not found!\n", debugstr_a(urlA));
3227 SetLastError(ERROR_FILE_NOT_FOUND);
3231 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3233 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3238 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3240 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3244 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3246 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3250 /***********************************************************************
3251 * CreateCacheContainerA (WININET.@)
3253 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3254 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3256 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3257 d1, d2, d3, d4, d5, d6, d7, d8);
3261 /***********************************************************************
3262 * CreateCacheContainerW (WININET.@)
3264 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3265 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3267 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3268 d1, d2, d3, d4, d5, d6, d7, d8);
3272 /***********************************************************************
3273 * FindFirstUrlCacheContainerA (WININET.@)
3275 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3277 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3281 /***********************************************************************
3282 * FindFirstUrlCacheContainerW (WININET.@)
3284 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3286 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3290 /***********************************************************************
3291 * FindNextUrlCacheContainerA (WININET.@)
3293 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3295 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3299 /***********************************************************************
3300 * FindNextUrlCacheContainerW (WININET.@)
3302 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3304 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3308 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3309 LPCSTR lpszUrlSearchPattern,
3313 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3314 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3316 LPDWORD pcbReserved2,
3320 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3321 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3322 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3323 SetLastError(ERROR_FILE_NOT_FOUND);
3327 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3328 LPCWSTR lpszUrlSearchPattern,
3332 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3333 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3335 LPDWORD pcbReserved2,
3339 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3340 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3341 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3342 SetLastError(ERROR_FILE_NOT_FOUND);
3346 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3348 typedef struct URLCacheFindEntryHandle
3351 LPWSTR lpszUrlSearchPattern;
3352 DWORD dwContainerIndex;
3353 DWORD dwHashTableIndex;
3354 DWORD dwHashEntryIndex;
3355 } URLCacheFindEntryHandle;
3357 /***********************************************************************
3358 * FindFirstUrlCacheEntryA (WININET.@)
3361 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3362 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3364 URLCacheFindEntryHandle *pEntryHandle;
3366 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3368 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3372 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3373 if (lpszUrlSearchPattern)
3375 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3376 if (!pEntryHandle->lpszUrlSearchPattern)
3378 heap_free(pEntryHandle);
3383 pEntryHandle->lpszUrlSearchPattern = NULL;
3384 pEntryHandle->dwContainerIndex = 0;
3385 pEntryHandle->dwHashTableIndex = 0;
3386 pEntryHandle->dwHashEntryIndex = 0;
3388 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3390 heap_free(pEntryHandle);
3393 return pEntryHandle;
3396 /***********************************************************************
3397 * FindFirstUrlCacheEntryW (WININET.@)
3400 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3401 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3403 URLCacheFindEntryHandle *pEntryHandle;
3405 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3407 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3411 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3412 if (lpszUrlSearchPattern)
3414 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3415 if (!pEntryHandle->lpszUrlSearchPattern)
3417 heap_free(pEntryHandle);
3422 pEntryHandle->lpszUrlSearchPattern = NULL;
3423 pEntryHandle->dwContainerIndex = 0;
3424 pEntryHandle->dwHashTableIndex = 0;
3425 pEntryHandle->dwHashEntryIndex = 0;
3427 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3429 heap_free(pEntryHandle);
3432 return pEntryHandle;
3435 static BOOL FindNextUrlCacheEntryInternal(
3437 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3438 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3441 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3442 URLCACHECONTAINER * pContainer;
3444 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3446 SetLastError(ERROR_INVALID_HANDLE);
3450 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3451 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3453 LPURLCACHE_HEADER pHeader;
3454 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3457 error = URLCacheContainer_OpenIndex(pContainer);
3458 if (error != ERROR_SUCCESS)
3460 SetLastError(error);
3464 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3467 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3468 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3470 const struct _HASH_ENTRY *pHashEntry = NULL;
3471 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3472 pEntryHandle->dwHashEntryIndex++)
3474 const URL_CACHEFILE_ENTRY *pUrlEntry;
3475 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3477 if (pEntry->dwSignature != URL_SIGNATURE)
3480 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3481 TRACE("Found URL: %s\n",
3482 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3483 TRACE("Header info: %s\n",
3484 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3486 error = URLCache_CopyEntry(
3489 lpNextCacheEntryInfo,
3490 lpdwNextCacheEntryInfoBufferSize,
3493 if (error != ERROR_SUCCESS)
3495 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3496 SetLastError(error);
3499 TRACE("Local File Name: %s\n",
3500 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3502 /* increment the current index so that next time the function
3503 * is called the next entry is returned */
3504 pEntryHandle->dwHashEntryIndex++;
3505 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3510 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3513 SetLastError(ERROR_NO_MORE_ITEMS);
3517 /***********************************************************************
3518 * FindNextUrlCacheEntryA (WININET.@)
3520 BOOL WINAPI FindNextUrlCacheEntryA(
3522 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3523 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3525 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3527 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3528 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3531 /***********************************************************************
3532 * FindNextUrlCacheEntryW (WININET.@)
3534 BOOL WINAPI FindNextUrlCacheEntryW(
3536 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3537 LPDWORD lpdwNextCacheEntryInfoBufferSize
3540 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3542 return FindNextUrlCacheEntryInternal(hEnumHandle,
3543 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3544 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3547 /***********************************************************************
3548 * FindCloseUrlCache (WININET.@)
3550 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3552 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3554 TRACE("(%p)\n", hEnumHandle);
3556 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3558 SetLastError(ERROR_INVALID_HANDLE);
3562 pEntryHandle->dwMagic = 0;
3563 heap_free(pEntryHandle->lpszUrlSearchPattern);
3564 heap_free(pEntryHandle);
3568 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3569 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3571 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3572 dwSearchCondition, lpGroupId, lpReserved);
3576 BOOL WINAPI FindNextUrlCacheEntryExA(
3578 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3579 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3581 LPDWORD pcbReserved2,
3585 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3586 lpReserved, pcbReserved2, lpReserved3);
3590 BOOL WINAPI FindNextUrlCacheEntryExW(
3592 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3593 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3595 LPDWORD pcbReserved2,
3599 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3600 lpReserved, pcbReserved2, lpReserved3);
3604 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3606 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3610 /***********************************************************************
3611 * CreateUrlCacheGroup (WININET.@)
3614 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3616 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3620 /***********************************************************************
3621 * DeleteUrlCacheGroup (WININET.@)
3624 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3626 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3627 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3631 /***********************************************************************
3632 * SetUrlCacheEntryGroupA (WININET.@)
3635 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3636 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3639 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3640 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3641 pbGroupAttributes, cbGroupAttributes, lpReserved);
3642 SetLastError(ERROR_FILE_NOT_FOUND);
3646 /***********************************************************************
3647 * SetUrlCacheEntryGroupW (WININET.@)
3650 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3651 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3654 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3655 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3656 pbGroupAttributes, cbGroupAttributes, lpReserved);
3657 SetLastError(ERROR_FILE_NOT_FOUND);
3661 /***********************************************************************
3662 * GetUrlCacheConfigInfoW (WININET.@)
3664 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3666 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3667 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3671 /***********************************************************************
3672 * GetUrlCacheConfigInfoA (WININET.@)
3674 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3676 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3677 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3681 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3682 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3683 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3685 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3686 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3687 lpdwGroupInfo, lpReserved);
3691 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3692 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3693 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3695 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3696 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3697 lpdwGroupInfo, lpReserved);
3701 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3702 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3704 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3705 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3709 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3710 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3712 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3713 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3717 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3719 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3723 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3725 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3729 /***********************************************************************
3730 * DeleteIE3Cache (WININET.@)
3732 * Deletes the files used by the IE3 URL caching system.
3735 * hWnd [I] A dummy window.
3736 * hInst [I] Instance of process calling the function.
3737 * lpszCmdLine [I] Options used by function.
3738 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3740 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3742 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3746 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3747 FILETIME *pftLastModified)
3750 FILETIME now, expired;
3752 *pftLastModified = pUrlEntry->LastModifiedTime;
3753 GetSystemTimeAsFileTime(&now);
3754 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3755 pUrlEntry->wExpiredTime, &expired);
3756 /* If the expired time is 0, it's interpreted as not expired */
3757 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3760 ret = CompareFileTime(&expired, &now) < 0;
3764 /***********************************************************************
3765 * IsUrlCacheEntryExpiredA (WININET.@)
3769 * dwFlags [I] Unknown
3770 * pftLastModified [O] Last modified time
3772 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3774 LPURLCACHE_HEADER pHeader;
3775 struct _HASH_ENTRY * pHashEntry;
3776 const CACHEFILE_ENTRY * pEntry;
3777 const URL_CACHEFILE_ENTRY * pUrlEntry;
3778 URLCACHECONTAINER * pContainer;
3781 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3783 if (!url || !pftLastModified)
3786 FIXME("unknown flags 0x%08x\n", dwFlags);
3788 /* Any error implies that the URL is expired, i.e. not in the cache */
3789 if (URLCacheContainers_FindContainerA(url, &pContainer))
3791 memset(pftLastModified, 0, sizeof(*pftLastModified));
3795 if (URLCacheContainer_OpenIndex(pContainer))
3797 memset(pftLastModified, 0, sizeof(*pftLastModified));
3801 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3803 memset(pftLastModified, 0, sizeof(*pftLastModified));
3807 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3809 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3810 memset(pftLastModified, 0, sizeof(*pftLastModified));
3811 TRACE("entry %s not found!\n", url);
3815 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3816 if (pEntry->dwSignature != URL_SIGNATURE)
3818 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3819 memset(pftLastModified, 0, sizeof(*pftLastModified));
3820 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3824 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3825 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3827 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3832 /***********************************************************************
3833 * IsUrlCacheEntryExpiredW (WININET.@)
3837 * dwFlags [I] Unknown
3838 * pftLastModified [O] Last modified time
3840 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3842 LPURLCACHE_HEADER pHeader;
3843 struct _HASH_ENTRY * pHashEntry;
3844 const CACHEFILE_ENTRY * pEntry;
3845 const URL_CACHEFILE_ENTRY * pUrlEntry;
3846 URLCACHECONTAINER * pContainer;
3849 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3851 if (!url || !pftLastModified)
3854 FIXME("unknown flags 0x%08x\n", dwFlags);
3856 /* Any error implies that the URL is expired, i.e. not in the cache */
3857 if (URLCacheContainers_FindContainerW(url, &pContainer))
3859 memset(pftLastModified, 0, sizeof(*pftLastModified));
3863 if (URLCacheContainer_OpenIndex(pContainer))
3865 memset(pftLastModified, 0, sizeof(*pftLastModified));
3869 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3871 memset(pftLastModified, 0, sizeof(*pftLastModified));
3875 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3877 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3878 memset(pftLastModified, 0, sizeof(*pftLastModified));
3879 TRACE("entry %s not found!\n", debugstr_w(url));
3883 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3885 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3886 memset(pftLastModified, 0, sizeof(*pftLastModified));
3887 TRACE("entry %s not found!\n", debugstr_w(url));
3891 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3892 if (pEntry->dwSignature != URL_SIGNATURE)
3894 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3895 memset(pftLastModified, 0, sizeof(*pftLastModified));
3896 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3900 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3901 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3903 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3908 /***********************************************************************
3909 * GetDiskInfoA (WININET.@)
3911 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3914 ULARGE_INTEGER bytes_free, bytes_total;
3916 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3920 SetLastError(ERROR_INVALID_PARAMETER);
3924 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3926 if (cluster_size) *cluster_size = 1;
3927 if (free) *free = bytes_free.QuadPart;
3928 if (total) *total = bytes_total.QuadPart;
3933 /***********************************************************************
3934 * RegisterUrlCacheNotification (WININET.@)
3936 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3938 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3942 /***********************************************************************
3943 * IncrementUrlCacheHeaderData (WININET.@)
3945 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3947 FIXME("(%u, %p)\n", index, data);
3951 /***********************************************************************
3952 * RunOnceUrlCache (WININET.@)
3955 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
3957 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);