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_BLOCKSIZE 7
68 #define HASHTABLE_FREE 3
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
71 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
72 #define NEWFILE_NUM_BLOCKS 0xd80
73 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
75 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
76 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
77 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
78 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
79 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
81 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
83 typedef struct _CACHEFILE_ENTRY
87 DWORD dwSignature; /* e.g. "URL " */
88 /* CHAR szSignature[4];
90 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
93 typedef struct _URL_CACHEFILE_ENTRY
95 CACHEFILE_ENTRY CacheFileEntry;
96 FILETIME LastModifiedTime;
97 FILETIME LastAccessTime;
98 WORD wExpiredDate; /* expire date in dos format */
99 WORD wExpiredTime; /* expire time in dos format */
100 DWORD dwUnknown1; /* usually zero */
101 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
102 DWORD dwUnknown2; /* usually zero */
103 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
104 DWORD dwUnknown3; /* usually 0x60 */
105 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
106 BYTE CacheDir; /* index of cache directory this url is stored in */
107 BYTE Unknown4; /* usually zero */
108 WORD wUnknown5; /* usually 0x1010 */
109 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
110 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
111 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
112 DWORD dwHeaderInfoSize;
113 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
114 WORD wLastSyncDate; /* last sync date in dos format */
115 WORD wLastSyncTime; /* last sync time in dos format */
116 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
117 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
118 WORD wUnknownDate; /* usually same as wLastSyncDate */
119 WORD wUnknownTime; /* usually same as wLastSyncTime */
120 DWORD dwUnknown7; /* usually zero */
121 DWORD dwUnknown8; /* usually zero */
122 /* packing to dword align start of next field */
123 /* CHAR szSourceUrlName[]; (url) */
124 /* packing to dword align start of next field */
125 /* CHAR szLocalFileName[]; (local file name excluding path) */
126 /* packing to dword align start of next field */
127 /* CHAR szHeaderInfo[]; (header info) */
128 } URL_CACHEFILE_ENTRY;
136 typedef struct _HASH_CACHEFILE_ENTRY
138 CACHEFILE_ENTRY CacheFileEntry;
140 DWORD dwHashTableNumber;
141 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
142 } HASH_CACHEFILE_ENTRY;
144 typedef struct _DIRECTORY_DATA
147 char filename[DIR_LENGTH];
150 typedef struct _URLCACHE_HEADER
152 char szSignature[28];
154 DWORD dwOffsetFirstHashTable;
155 DWORD dwIndexCapacityInBlocks;
158 ULARGE_INTEGER CacheLimit;
159 ULARGE_INTEGER CacheUsage;
160 ULARGE_INTEGER ExemptUsage;
161 DWORD DirectoryCount; /* number of directory_data's */
162 DIRECTORY_DATA directory_data[1]; /* first directory entry */
163 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
164 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
166 typedef struct _STREAM_HANDLE
172 typedef struct _URLCACHECONTAINER
174 struct list entry; /* part of a list */
175 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
176 LPWSTR path; /* path to url container directory */
177 HANDLE hMapping; /* handle of file mapping */
178 DWORD file_size; /* size of file when mapping was opened */
179 HANDLE hMutex; /* handle of mutex */
183 /* List of all containers available */
184 static struct list UrlContainers = LIST_INIT(UrlContainers);
186 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
188 /***********************************************************************
189 * URLCache_PathToObjectName (Internal)
191 * Converts a path to a name suitable for use as a Win32 object name.
192 * Replaces '\\' characters in-place with the specified character
193 * (usually '_' or '!')
199 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
201 for (; *lpszPath; lpszPath++)
203 if (*lpszPath == '\\')
208 /***********************************************************************
209 * URLCacheContainer_OpenIndex (Internal)
211 * Opens the index file and saves mapping handle in hCacheIndexMapping
214 * ERROR_SUCCESS if succeeded
215 * Any other Win32 error code if failed
218 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
221 WCHAR wszFilePath[MAX_PATH];
224 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
225 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
227 WaitForSingleObject(pContainer->hMutex, INFINITE);
229 if (pContainer->hMapping) {
230 ReleaseMutex(pContainer->hMutex);
231 return ERROR_SUCCESS;
234 strcpyW(wszFilePath, pContainer->path);
235 strcatW(wszFilePath, wszIndex);
237 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
238 if (hFile == INVALID_HANDLE_VALUE)
240 /* Maybe the directory wasn't there? Try to create it */
241 if (CreateDirectoryW(pContainer->path, 0))
242 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 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
247 ReleaseMutex(pContainer->hMutex);
248 return GetLastError();
251 dwFileSize = GetFileSize(hFile, NULL);
252 if (dwFileSize == INVALID_FILE_SIZE)
254 ReleaseMutex(pContainer->hMutex);
255 return GetLastError();
260 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
262 char achZeroes[0x1000];
264 DWORD dwError = ERROR_SUCCESS;
266 /* Write zeroes to the entire file so we can safely map it without
267 * fear of getting a SEGV because the disk is full.
269 memset(achZeroes, 0, sizeof(achZeroes));
270 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
272 DWORD dwWrite = sizeof(achZeroes);
275 if (NEWFILE_SIZE - dwOffset < dwWrite)
276 dwWrite = NEWFILE_SIZE - dwOffset;
277 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
278 dwWritten != dwWrite)
280 /* If we fail to write, we need to return the error that
281 * cause the problem and also make sure the file is no
282 * longer there, if possible.
284 dwError = GetLastError();
290 if (dwError == ERROR_SUCCESS)
292 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
296 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
301 WCHAR wszDirPath[MAX_PATH];
304 HASH_CACHEFILE_ENTRY *pHashEntry;
306 dwFileSize = NEWFILE_SIZE;
308 /* First set some constants and defaults in the header */
309 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
310 pHeader->dwFileSize = dwFileSize;
311 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
312 /* 127MB - taken from default for Windows 2000 */
313 pHeader->CacheLimit.QuadPart = 0x07ff5400;
314 /* Copied from a Windows 2000 cache index */
315 pHeader->DirectoryCount = 4;
317 /* If the registry has a cache size set, use the registry value */
318 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
321 DWORD len = sizeof(dw);
324 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
325 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
326 keytype == REG_DWORD)
328 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
333 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
335 /* Last step - create the directories */
337 strcpyW(wszDirPath, pContainer->path);
338 pwchDir = wszDirPath + strlenW(wszDirPath);
341 GetSystemTimeAsFileTime(&ft);
343 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
345 pHeader->directory_data[i].dwNumFiles = 0;
349 ULONGLONG n = ft.dwHighDateTime;
351 /* Generate a file name to attempt to create.
352 * This algorithm will create what will appear
353 * to be random and unrelated directory names
354 * of up to 9 characters in length.
357 n += ft.dwLowDateTime;
358 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
360 for (k = 0; k < 8; ++k)
364 /* Dividing by a prime greater than 36 helps
365 * with the appearance of randomness
370 pwchDir[k] = '0' + r;
372 pwchDir[k] = 'A' + (r - 10);
375 if (CreateDirectoryW(wszDirPath, 0))
377 /* The following is OK because we generated an
378 * 8 character directory name made from characters
379 * [A-Z0-9], which are equivalent for all code
380 * pages and for UTF-16
382 for (k = 0; k < 8; ++k)
383 pHeader->directory_data[i].filename[k] = pwchDir[k];
388 /* Give up. The most likely cause of this
389 * is a full disk, but whatever the cause
390 * is, it should be more than apparent that
393 dwError = GetLastError();
399 UnmapViewOfFile(pHeader);
403 dwError = GetLastError();
405 CloseHandle(hMapping);
409 dwError = GetLastError();
416 DeleteFileW(wszFilePath);
417 ReleaseMutex(pContainer->hMutex);
423 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
424 URLCache_PathToObjectName(wszFilePath, '_');
425 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
426 if (!pContainer->hMapping)
427 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
429 if (!pContainer->hMapping)
431 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
432 ReleaseMutex(pContainer->hMutex);
433 return GetLastError();
436 ReleaseMutex(pContainer->hMutex);
438 return ERROR_SUCCESS;
441 /***********************************************************************
442 * URLCacheContainer_CloseIndex (Internal)
450 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
452 CloseHandle(pContainer->hMapping);
453 pContainer->hMapping = NULL;
456 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
458 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
459 int cache_prefix_len = strlenW(cache_prefix);
466 pContainer->hMapping = NULL;
467 pContainer->file_size = 0;
469 pContainer->path = heap_strdupW(path);
470 if (!pContainer->path)
472 HeapFree(GetProcessHeap(), 0, pContainer);
476 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
477 if (!pContainer->cache_prefix)
479 HeapFree(GetProcessHeap(), 0, pContainer->path);
480 HeapFree(GetProcessHeap(), 0, pContainer);
484 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
486 CharLowerW(mutex_name);
487 URLCache_PathToObjectName(mutex_name, '!');
489 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
491 ERR("couldn't create mutex (error is %d)\n", GetLastError());
492 HeapFree(GetProcessHeap(), 0, pContainer->path);
493 HeapFree(GetProcessHeap(), 0, pContainer);
497 list_add_head(&UrlContainers, &pContainer->entry);
502 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
504 list_remove(&pContainer->entry);
506 URLCacheContainer_CloseIndex(pContainer);
507 CloseHandle(pContainer->hMutex);
508 HeapFree(GetProcessHeap(), 0, pContainer->path);
509 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
510 HeapFree(GetProcessHeap(), 0, pContainer);
513 void URLCacheContainers_CreateDefaults(void)
515 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
516 static const WCHAR UrlPrefix[] = {0};
517 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
518 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
519 static const WCHAR CookieSuffix[] = {0};
520 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
523 int nFolder; /* CSIDL_* constant */
524 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
525 const WCHAR * cache_prefix; /* prefix used to reference the container */
526 } DefaultContainerData[] =
528 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
529 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
530 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
534 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
536 WCHAR wszCachePath[MAX_PATH];
537 WCHAR wszMutexName[MAX_PATH];
538 int path_len, suffix_len;
540 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
542 ERR("Couldn't get path for default container %u\n", i);
545 path_len = strlenW(wszCachePath);
546 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
548 if (path_len + suffix_len + 2 > MAX_PATH)
550 ERR("Path too long\n");
554 wszCachePath[path_len] = '\\';
555 wszCachePath[path_len+1] = 0;
557 strcpyW(wszMutexName, wszCachePath);
561 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
562 wszCachePath[path_len + suffix_len + 1] = '\\';
563 wszCachePath[path_len + suffix_len + 2] = '\0';
566 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
570 void URLCacheContainers_DeleteAll(void)
572 while(!list_empty(&UrlContainers))
573 URLCacheContainer_DeleteContainer(
574 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
578 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
580 URLCACHECONTAINER * pContainer;
582 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
585 return ERROR_INVALID_PARAMETER;
587 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
589 int prefix_len = strlenW(pContainer->cache_prefix);
590 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
592 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
593 *ppContainer = pContainer;
594 return ERROR_SUCCESS;
597 ERR("no container found\n");
598 return ERROR_FILE_NOT_FOUND;
601 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
606 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
607 return ERROR_OUTOFMEMORY;
609 ret = URLCacheContainers_FindContainerW(url, ppContainer);
610 HeapFree(GetProcessHeap(), 0, url);
614 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
617 URLCACHECONTAINER * pContainer;
619 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
621 /* non-NULL search pattern only returns one container ever */
622 if (lpwszSearchPattern && dwIndex > 0)
625 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
627 if (lpwszSearchPattern)
629 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
631 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
632 *ppContainer = pContainer;
640 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
641 *ppContainer = pContainer;
650 /***********************************************************************
651 * URLCacheContainer_LockIndex (Internal)
653 * Locks the index for system-wide exclusive access.
656 * Cache file header if successful
657 * NULL if failed and calls SetLastError.
659 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
663 URLCACHE_HEADER * pHeader;
667 WaitForSingleObject(pContainer->hMutex, INFINITE);
669 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
673 ReleaseMutex(pContainer->hMutex);
674 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
677 pHeader = (URLCACHE_HEADER *)pIndexData;
679 /* file has grown - we need to remap to prevent us getting
680 * access violations when we try and access beyond the end
681 * of the memory mapped file */
682 if (pHeader->dwFileSize != pContainer->file_size)
684 UnmapViewOfFile( pHeader );
685 URLCacheContainer_CloseIndex(pContainer);
686 error = URLCacheContainer_OpenIndex(pContainer);
687 if (error != ERROR_SUCCESS)
689 ReleaseMutex(pContainer->hMutex);
693 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
697 ReleaseMutex(pContainer->hMutex);
698 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
701 pHeader = (URLCACHE_HEADER *)pIndexData;
704 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
706 for (index = 0; index < pHeader->DirectoryCount; index++)
708 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
714 /***********************************************************************
715 * URLCacheContainer_UnlockIndex (Internal)
718 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
721 ReleaseMutex(pContainer->hMutex);
722 return UnmapViewOfFile(pHeader);
727 #define CHAR_BIT (8 * sizeof(CHAR))
730 /***********************************************************************
731 * URLCache_Allocation_BlockIsFree (Internal)
733 * Is the specified block number free?
740 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
742 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
743 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
746 /***********************************************************************
747 * URLCache_Allocation_BlockFree (Internal)
749 * Marks the specified block as free
755 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
757 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
758 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
761 /***********************************************************************
762 * URLCache_Allocation_BlockAllocate (Internal)
764 * Marks the specified block as allocated
770 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
772 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
773 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
776 /***********************************************************************
777 * URLCache_FindFirstFreeEntry (Internal)
779 * Finds and allocates the first block of free space big enough and
780 * sets ppEntry to point to it.
783 * TRUE if it had enough space
784 * FALSE if it couldn't find enough space
787 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
789 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
792 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
794 for (dwFreeCounter = 0;
795 dwFreeCounter < dwBlocksNeeded &&
796 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
797 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
799 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
801 if (dwFreeCounter == dwBlocksNeeded)
804 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
805 for (index = 0; index < dwBlocksNeeded; index++)
806 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
807 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
808 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
812 FIXME("Grow file\n");
816 /***********************************************************************
817 * URLCache_DeleteEntry (Internal)
819 * Deletes the specified entry and frees the space allocated to it
822 * TRUE if it succeeded
826 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
830 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
832 /* update allocation table */
833 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
834 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
835 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
837 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
841 /***********************************************************************
842 * URLCache_LocalFileNameToPathW (Internal)
844 * Copies the full path to the specified buffer given the local file
845 * name and the index of the directory it is in. Always sets value in
846 * lpBufferSize to the required buffer size (in bytes).
849 * TRUE if the buffer was big enough
850 * FALSE if the buffer was too small
853 static BOOL URLCache_LocalFileNameToPathW(
854 const URLCACHECONTAINER * pContainer,
855 LPCURLCACHE_HEADER pHeader,
856 LPCSTR szLocalFileName,
862 int path_len = strlenW(pContainer->path);
863 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
864 if (Directory >= pHeader->DirectoryCount)
870 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
871 if (nRequired <= *lpBufferSize)
875 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
876 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
877 wszPath[dir_len + path_len] = '\\';
878 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
879 *lpBufferSize = nRequired;
882 *lpBufferSize = nRequired;
886 /***********************************************************************
887 * URLCache_LocalFileNameToPathA (Internal)
889 * Copies the full path to the specified buffer given the local file
890 * name and the index of the directory it is in. Always sets value in
891 * lpBufferSize to the required buffer size.
894 * TRUE if the buffer was big enough
895 * FALSE if the buffer was too small
898 static BOOL URLCache_LocalFileNameToPathA(
899 const URLCACHECONTAINER * pContainer,
900 LPCURLCACHE_HEADER pHeader,
901 LPCSTR szLocalFileName,
907 int path_len, file_name_len, dir_len;
909 if (Directory >= pHeader->DirectoryCount)
915 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
916 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
917 dir_len = DIR_LENGTH;
919 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
920 if (nRequired < *lpBufferSize)
922 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
923 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
924 szPath[path_len + dir_len] = '\\';
925 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
926 *lpBufferSize = nRequired;
929 *lpBufferSize = nRequired;
933 /* Just like DosDateTimeToFileTime, except that it also maps the special
934 * case of a DOS date/time of (0,0) to a filetime of (0,0).
936 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
939 if (!fatdate && !fattime)
940 ft->dwLowDateTime = ft->dwHighDateTime = 0;
942 DosDateTimeToFileTime(fatdate, fattime, ft);
945 /***********************************************************************
946 * URLCache_CopyEntry (Internal)
948 * Copies an entry from the cache index file to the Win32 structure
951 * ERROR_SUCCESS if the buffer was big enough
952 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
955 static DWORD URLCache_CopyEntry(
956 URLCACHECONTAINER * pContainer,
957 LPCURLCACHE_HEADER pHeader,
958 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
959 LPDWORD lpdwBufferSize,
960 const URL_CACHEFILE_ENTRY * pUrlEntry,
964 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
966 if (*lpdwBufferSize >= dwRequiredSize)
968 lpCacheEntryInfo->lpHeaderInfo = NULL;
969 lpCacheEntryInfo->lpszFileExtension = NULL;
970 lpCacheEntryInfo->lpszLocalFileName = NULL;
971 lpCacheEntryInfo->lpszSourceUrlName = NULL;
972 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
973 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
974 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
975 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
976 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
977 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
978 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
979 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
980 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
981 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
982 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
983 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
984 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
985 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
988 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
989 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
990 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
992 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
994 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
995 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
997 /* FIXME: is source url optional? */
998 if (*lpdwBufferSize >= dwRequiredSize)
1000 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1002 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1004 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1006 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1009 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1010 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1011 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1013 if (pUrlEntry->dwOffsetLocalName)
1015 LONG nLocalFilePathSize;
1016 LPSTR lpszLocalFileName;
1017 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1018 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1019 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1020 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1022 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1024 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1026 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1027 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1028 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1030 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1032 if (*lpdwBufferSize >= dwRequiredSize)
1034 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1035 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1036 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1038 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1039 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1040 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1042 if (pUrlEntry->dwOffsetFileExtension)
1047 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1049 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1050 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1052 if (*lpdwBufferSize >= dwRequiredSize)
1054 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1056 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1058 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1061 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1062 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1063 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1066 if (dwRequiredSize > *lpdwBufferSize)
1068 *lpdwBufferSize = dwRequiredSize;
1069 return ERROR_INSUFFICIENT_BUFFER;
1071 *lpdwBufferSize = dwRequiredSize;
1072 return ERROR_SUCCESS;
1075 /* Just like FileTimeToDosDateTime, except that it also maps the special
1076 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1078 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1081 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1082 *fatdate = *fattime = 0;
1084 FileTimeToDosDateTime(ft, fatdate, fattime);
1087 /***********************************************************************
1088 * URLCache_SetEntryInfo (Internal)
1090 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1091 * according to the flags set by dwFieldControl.
1094 * ERROR_SUCCESS if the buffer was big enough
1095 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1098 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1100 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1101 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1102 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1103 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1104 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1105 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1106 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1107 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1108 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1109 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1110 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1111 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1112 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1113 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1114 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1115 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1117 return ERROR_SUCCESS;
1120 /***********************************************************************
1121 * URLCache_HashKey (Internal)
1123 * Returns the hash key for a given string
1126 * hash key for the string
1129 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1131 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1132 * but the algorithm and result are not the same!
1134 static const unsigned char lookupTable[256] =
1136 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1137 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1138 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1139 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1140 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1141 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1142 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1143 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1144 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1145 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1146 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1147 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1148 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1149 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1150 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1151 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1152 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1153 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1154 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1155 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1156 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1157 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1158 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1159 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1160 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1161 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1162 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1163 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1164 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1165 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1166 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1167 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1172 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1173 key[i] = lookupTable[i];
1175 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1177 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1178 key[i] = lookupTable[*lpszKey ^ key[i]];
1181 return *(DWORD *)key;
1184 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1186 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1189 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1191 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1192 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1193 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1196 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1198 /* structure of hash table:
1199 * 448 entries divided into 64 blocks
1200 * each block therefore contains a chain of 7 key/offset pairs
1201 * how position in table is calculated:
1202 * 1. the url is hashed in helper function
1203 * 2. the key % 64 * 8 is the offset
1204 * 3. the key in the hash table is the hash key aligned to 64
1207 * there can be multiple hash tables in the file and the offset to
1208 * the next one is stored in the header of the hash table
1210 DWORD key = URLCache_HashKey(lpszUrl);
1211 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1212 HASH_CACHEFILE_ENTRY * pHashEntry;
1213 DWORD dwHashTableNumber = 0;
1215 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1217 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1218 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1219 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1222 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1224 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1227 /* make sure that it is in fact a hash entry */
1228 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1230 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1234 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1236 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1237 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1239 /* FIXME: we should make sure that this is the right element
1240 * before returning and claiming that it is. We can do this
1241 * by doing a simple compare between the URL we were given
1242 * and the URL stored in the entry. However, this assumes
1243 * we know the format of all the entries stored in the
1245 *ppHashEntry = pHashElement;
1253 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1258 urlA = heap_strdupWtoA(lpszUrl);
1261 SetLastError(ERROR_OUTOFMEMORY);
1265 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1266 HeapFree(GetProcessHeap(), 0, urlA);
1270 /***********************************************************************
1271 * URLCache_HashEntrySetUse (Internal)
1273 * Searches all the hash tables in the index for the given URL and
1274 * sets the use count (stored or'ed with key)
1277 * TRUE if the entry was found
1278 * FALSE if the entry could not be found
1281 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1283 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1287 /***********************************************************************
1288 * URLCache_DeleteEntryFromHash (Internal)
1290 * Searches all the hash tables in the index for the given URL and
1291 * then if found deletes the entry.
1294 * TRUE if the entry was found
1295 * FALSE if the entry could not be found
1298 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1300 pHashEntry->dwHashKey = HASHTABLE_FREE;
1301 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1305 /***********************************************************************
1306 * URLCache_AddEntryToHash (Internal)
1308 * Searches all the hash tables for a free slot based on the offset
1309 * generated from the hash key. If a free slot is found, the offset and
1310 * key are entered into the hash table.
1313 * ERROR_SUCCESS if the entry was added
1314 * Any other Win32 error code if the entry could not be added
1317 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1319 /* see URLCache_FindEntryInHash for structure of hash tables */
1321 DWORD key = URLCache_HashKey(lpszUrl);
1322 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1323 HASH_CACHEFILE_ENTRY * pHashEntry;
1324 DWORD dwHashTableNumber = 0;
1327 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1329 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1330 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1331 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1334 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1336 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1339 /* make sure that it is in fact a hash entry */
1340 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1342 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1346 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1348 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1349 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1351 pHashElement->dwHashKey = key;
1352 pHashElement->dwOffsetEntry = dwOffsetEntry;
1353 return ERROR_SUCCESS;
1357 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1358 if (error != ERROR_SUCCESS)
1361 pHashEntry->HashTable[offset].dwHashKey = key;
1362 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1363 return ERROR_SUCCESS;
1366 /***********************************************************************
1367 * URLCache_CreateHashTable (Internal)
1369 * Creates a new hash table in free space and adds it to the chain of existing
1373 * ERROR_SUCCESS if the hash table was created
1374 * ERROR_DISK_FULL if the hash table could not be created
1377 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1382 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1384 FIXME("no free space for hash table\n");
1385 return ERROR_DISK_FULL;
1388 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1391 pPrevHash->dwAddressNext = dwOffset;
1393 pHeader->dwOffsetFirstHashTable = dwOffset;
1394 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1395 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1396 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1397 for (i = 0; i < HASHTABLE_SIZE; i++)
1399 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1400 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1402 return ERROR_SUCCESS;
1405 /***********************************************************************
1406 * URLCache_EnumHashTables (Internal)
1408 * Enumerates the hash tables in a container.
1411 * TRUE if an entry was found
1412 * FALSE if there are no more tables to enumerate.
1415 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1417 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1418 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1419 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1421 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1422 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1424 /* make sure that it is in fact a hash entry */
1425 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1427 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1428 (*pdwHashTableNumber)++;
1432 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1438 /***********************************************************************
1439 * URLCache_EnumHashTableEntries (Internal)
1441 * Enumerates entries in a hash table and returns the next non-free entry.
1444 * TRUE if an entry was found
1445 * FALSE if the hash table is empty or there are no more entries to
1449 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1450 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1452 for (; *index < HASHTABLE_SIZE ; (*index)++)
1454 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1457 *ppHashEntry = &pHashEntry->HashTable[*index];
1458 TRACE("entry found %d\n", *index);
1461 TRACE("no more entries (%d)\n", *index);
1465 /***********************************************************************
1466 * URLCache_DeleteCacheDirectory (Internal)
1468 * Erase a directory containing an URL cache.
1471 * TRUE success, FALSE failure/aborted.
1474 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1477 WCHAR path[MAX_PATH + 1];
1478 SHFILEOPSTRUCTW shfos;
1481 path_len = strlenW(lpszPath);
1482 if (path_len >= MAX_PATH)
1484 strcpyW(path, lpszPath);
1485 path[path_len + 1] = 0; /* double-NUL-terminate path */
1488 shfos.wFunc = FO_DELETE;
1492 shfos.fAnyOperationsAborted = FALSE;
1493 ret = SHFileOperationW(&shfos);
1495 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1496 return !(ret || shfos.fAnyOperationsAborted);
1499 /***********************************************************************
1500 * FreeUrlCacheSpaceW (WININET.@)
1502 * Frees up some cache.
1505 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1506 * dwSize [I] How much space to free up.
1507 * dwSizeType [I] How to interpret dwSize.
1510 * TRUE success. FALSE failure.
1513 * This implementation just retrieves the path of the cache directory, and
1514 * deletes its contents from the filesystem. The correct approach would
1515 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1517 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1519 URLCACHECONTAINER * pContainer;
1521 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1523 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1524 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1528 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1530 /* The URL cache has prefix L"" (unlike Cookies and History) */
1531 if (pContainer->cache_prefix[0] == 0)
1535 WaitForSingleObject(pContainer->hMutex, INFINITE);
1537 /* unlock, delete, recreate and lock cache */
1538 URLCacheContainer_CloseIndex(pContainer);
1539 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1540 ret_open = URLCacheContainer_OpenIndex(pContainer);
1542 ReleaseMutex(pContainer->hMutex);
1543 return ret_del && (ret_open == ERROR_SUCCESS);
1549 /***********************************************************************
1550 * FreeUrlCacheSpaceA (WININET.@)
1552 * See FreeUrlCacheSpaceW.
1554 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1557 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1558 if (lpszCachePath == NULL || path != NULL)
1559 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1564 /***********************************************************************
1565 * GetUrlCacheEntryInfoExA (WININET.@)
1568 BOOL WINAPI GetUrlCacheEntryInfoExA(
1570 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1571 LPDWORD lpdwCacheEntryInfoBufSize,
1573 LPDWORD lpdwReserved,
1577 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1578 debugstr_a(lpszUrl),
1580 lpdwCacheEntryInfoBufSize,
1586 if ((lpszReserved != NULL) ||
1587 (lpdwReserved != NULL) ||
1588 (lpReserved != NULL))
1590 ERR("Reserved value was not 0\n");
1591 SetLastError(ERROR_INVALID_PARAMETER);
1596 FIXME("Undocumented flag(s): %x\n", dwFlags);
1597 SetLastError(ERROR_FILE_NOT_FOUND);
1600 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1603 /***********************************************************************
1604 * GetUrlCacheEntryInfoA (WININET.@)
1607 BOOL WINAPI GetUrlCacheEntryInfoA(
1608 IN LPCSTR lpszUrlName,
1609 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1610 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1613 LPURLCACHE_HEADER pHeader;
1614 struct _HASH_ENTRY * pHashEntry;
1615 const CACHEFILE_ENTRY * pEntry;
1616 const URL_CACHEFILE_ENTRY * pUrlEntry;
1617 URLCACHECONTAINER * pContainer;
1620 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1622 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1623 if (error != ERROR_SUCCESS)
1625 SetLastError(error);
1629 error = URLCacheContainer_OpenIndex(pContainer);
1630 if (error != ERROR_SUCCESS)
1632 SetLastError(error);
1636 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1639 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1641 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1642 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1643 SetLastError(ERROR_FILE_NOT_FOUND);
1647 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1648 if (pEntry->dwSignature != URL_SIGNATURE)
1650 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1651 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1652 SetLastError(ERROR_FILE_NOT_FOUND);
1656 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1657 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1658 if (pUrlEntry->dwOffsetHeaderInfo)
1659 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1661 if (lpdwCacheEntryInfoBufferSize)
1663 if (!lpCacheEntryInfo)
1664 *lpdwCacheEntryInfoBufferSize = 0;
1666 error = URLCache_CopyEntry(
1670 lpdwCacheEntryInfoBufferSize,
1673 if (error != ERROR_SUCCESS)
1675 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1676 SetLastError(error);
1679 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1682 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1687 /***********************************************************************
1688 * GetUrlCacheEntryInfoW (WININET.@)
1691 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1692 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1693 LPDWORD lpdwCacheEntryInfoBufferSize)
1695 LPURLCACHE_HEADER pHeader;
1696 struct _HASH_ENTRY * pHashEntry;
1697 const CACHEFILE_ENTRY * pEntry;
1698 const URL_CACHEFILE_ENTRY * pUrlEntry;
1699 URLCACHECONTAINER * pContainer;
1702 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1704 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1705 if (error != ERROR_SUCCESS)
1707 SetLastError(error);
1711 error = URLCacheContainer_OpenIndex(pContainer);
1712 if (error != ERROR_SUCCESS)
1714 SetLastError(error);
1718 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1721 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1723 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1724 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1725 SetLastError(ERROR_FILE_NOT_FOUND);
1729 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1730 if (pEntry->dwSignature != URL_SIGNATURE)
1732 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1733 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1734 SetLastError(ERROR_FILE_NOT_FOUND);
1738 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1739 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1740 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1742 if (lpdwCacheEntryInfoBufferSize)
1744 if (!lpCacheEntryInfo)
1745 *lpdwCacheEntryInfoBufferSize = 0;
1747 error = URLCache_CopyEntry(
1750 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1751 lpdwCacheEntryInfoBufferSize,
1753 TRUE /* UNICODE */);
1754 if (error != ERROR_SUCCESS)
1756 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1757 SetLastError(error);
1760 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1763 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1768 /***********************************************************************
1769 * GetUrlCacheEntryInfoExW (WININET.@)
1772 BOOL WINAPI GetUrlCacheEntryInfoExW(
1774 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1775 LPDWORD lpdwCacheEntryInfoBufSize,
1776 LPWSTR lpszReserved,
1777 LPDWORD lpdwReserved,
1781 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1782 debugstr_w(lpszUrl),
1784 lpdwCacheEntryInfoBufSize,
1790 if ((lpszReserved != NULL) ||
1791 (lpdwReserved != NULL) ||
1792 (lpReserved != NULL))
1794 ERR("Reserved value was not 0\n");
1795 SetLastError(ERROR_INVALID_PARAMETER);
1800 FIXME("Undocumented flag(s): %x\n", dwFlags);
1801 SetLastError(ERROR_FILE_NOT_FOUND);
1804 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1807 /***********************************************************************
1808 * SetUrlCacheEntryInfoA (WININET.@)
1810 BOOL WINAPI SetUrlCacheEntryInfoA(
1812 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1813 DWORD dwFieldControl)
1815 LPURLCACHE_HEADER pHeader;
1816 struct _HASH_ENTRY * pHashEntry;
1817 CACHEFILE_ENTRY * pEntry;
1818 URLCACHECONTAINER * pContainer;
1821 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1823 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1824 if (error != ERROR_SUCCESS)
1826 SetLastError(error);
1830 error = URLCacheContainer_OpenIndex(pContainer);
1831 if (error != ERROR_SUCCESS)
1833 SetLastError(error);
1837 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1840 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1842 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1843 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1844 SetLastError(ERROR_FILE_NOT_FOUND);
1848 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1849 if (pEntry->dwSignature != URL_SIGNATURE)
1851 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1852 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1853 SetLastError(ERROR_FILE_NOT_FOUND);
1857 URLCache_SetEntryInfo(
1858 (URL_CACHEFILE_ENTRY *)pEntry,
1859 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1862 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1867 /***********************************************************************
1868 * SetUrlCacheEntryInfoW (WININET.@)
1870 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1872 LPURLCACHE_HEADER pHeader;
1873 struct _HASH_ENTRY * pHashEntry;
1874 CACHEFILE_ENTRY * pEntry;
1875 URLCACHECONTAINER * pContainer;
1878 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1880 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1881 if (error != ERROR_SUCCESS)
1883 SetLastError(error);
1887 error = URLCacheContainer_OpenIndex(pContainer);
1888 if (error != ERROR_SUCCESS)
1890 SetLastError(error);
1894 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1897 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1899 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1900 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1901 SetLastError(ERROR_FILE_NOT_FOUND);
1905 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1906 if (pEntry->dwSignature != URL_SIGNATURE)
1908 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1909 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1910 SetLastError(ERROR_FILE_NOT_FOUND);
1914 URLCache_SetEntryInfo(
1915 (URL_CACHEFILE_ENTRY *)pEntry,
1919 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1924 /***********************************************************************
1925 * RetrieveUrlCacheEntryFileA (WININET.@)
1928 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1929 IN LPCSTR lpszUrlName,
1930 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1931 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1935 LPURLCACHE_HEADER pHeader;
1936 struct _HASH_ENTRY * pHashEntry;
1937 CACHEFILE_ENTRY * pEntry;
1938 URL_CACHEFILE_ENTRY * pUrlEntry;
1939 URLCACHECONTAINER * pContainer;
1942 TRACE("(%s, %p, %p, 0x%08x)\n",
1943 debugstr_a(lpszUrlName),
1945 lpdwCacheEntryInfoBufferSize,
1948 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1949 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1951 SetLastError(ERROR_INVALID_PARAMETER);
1955 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1956 if (error != ERROR_SUCCESS)
1958 SetLastError(error);
1962 error = URLCacheContainer_OpenIndex(pContainer);
1963 if (error != ERROR_SUCCESS)
1965 SetLastError(error);
1969 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1972 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1974 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1975 TRACE("entry %s not found!\n", lpszUrlName);
1976 SetLastError(ERROR_FILE_NOT_FOUND);
1980 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1981 if (pEntry->dwSignature != URL_SIGNATURE)
1983 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1984 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1985 SetLastError(ERROR_FILE_NOT_FOUND);
1989 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1990 if (!pUrlEntry->dwOffsetLocalName)
1992 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1993 SetLastError(ERROR_INVALID_DATA);
1997 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1998 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2000 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2001 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2003 if (error != ERROR_SUCCESS)
2005 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2006 SetLastError(error);
2009 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2011 pUrlEntry->dwHitRate++;
2012 pUrlEntry->dwUseCount++;
2013 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2014 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2016 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2021 /***********************************************************************
2022 * RetrieveUrlCacheEntryFileW (WININET.@)
2025 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2026 IN LPCWSTR lpszUrlName,
2027 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2028 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2032 LPURLCACHE_HEADER pHeader;
2033 struct _HASH_ENTRY * pHashEntry;
2034 CACHEFILE_ENTRY * pEntry;
2035 URL_CACHEFILE_ENTRY * pUrlEntry;
2036 URLCACHECONTAINER * pContainer;
2039 TRACE("(%s, %p, %p, 0x%08x)\n",
2040 debugstr_w(lpszUrlName),
2042 lpdwCacheEntryInfoBufferSize,
2045 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2046 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2048 SetLastError(ERROR_INVALID_PARAMETER);
2052 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2053 if (error != ERROR_SUCCESS)
2055 SetLastError(error);
2059 error = URLCacheContainer_OpenIndex(pContainer);
2060 if (error != ERROR_SUCCESS)
2062 SetLastError(error);
2066 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2069 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2071 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2072 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2073 SetLastError(ERROR_FILE_NOT_FOUND);
2077 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2078 if (pEntry->dwSignature != URL_SIGNATURE)
2080 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2081 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2082 SetLastError(ERROR_FILE_NOT_FOUND);
2086 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2087 if (!pUrlEntry->dwOffsetLocalName)
2089 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2090 SetLastError(ERROR_INVALID_DATA);
2094 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2095 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2097 error = URLCache_CopyEntry(
2100 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2101 lpdwCacheEntryInfoBufferSize,
2103 TRUE /* UNICODE */);
2104 if (error != ERROR_SUCCESS)
2106 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2107 SetLastError(error);
2110 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2112 pUrlEntry->dwHitRate++;
2113 pUrlEntry->dwUseCount++;
2114 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2115 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2117 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2122 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2123 struct _HASH_ENTRY *pHashEntry)
2125 CACHEFILE_ENTRY * pEntry;
2126 URL_CACHEFILE_ENTRY * pUrlEntry;
2128 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2129 if (pEntry->dwSignature != URL_SIGNATURE)
2131 FIXME("Trying to delete entry of unknown format %s\n",
2132 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2133 SetLastError(ERROR_FILE_NOT_FOUND);
2136 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2137 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2139 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2140 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2142 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2144 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2145 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2147 pHeader->ExemptUsage.QuadPart = 0;
2151 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2152 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2154 pHeader->CacheUsage.QuadPart = 0;
2157 URLCache_DeleteEntry(pHeader, pEntry);
2159 URLCache_DeleteEntryFromHash(pHashEntry);
2163 /***********************************************************************
2164 * UnlockUrlCacheEntryFileA (WININET.@)
2167 BOOL WINAPI UnlockUrlCacheEntryFileA(
2168 IN LPCSTR lpszUrlName,
2172 LPURLCACHE_HEADER pHeader;
2173 struct _HASH_ENTRY * pHashEntry;
2174 CACHEFILE_ENTRY * pEntry;
2175 URL_CACHEFILE_ENTRY * pUrlEntry;
2176 URLCACHECONTAINER * pContainer;
2179 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2183 ERR("dwReserved != 0\n");
2184 SetLastError(ERROR_INVALID_PARAMETER);
2188 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2189 if (error != ERROR_SUCCESS)
2191 SetLastError(error);
2195 error = URLCacheContainer_OpenIndex(pContainer);
2196 if (error != ERROR_SUCCESS)
2198 SetLastError(error);
2202 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2205 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2207 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2208 TRACE("entry %s not found!\n", lpszUrlName);
2209 SetLastError(ERROR_FILE_NOT_FOUND);
2213 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2214 if (pEntry->dwSignature != URL_SIGNATURE)
2216 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2217 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2218 SetLastError(ERROR_FILE_NOT_FOUND);
2222 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2224 if (pUrlEntry->dwUseCount == 0)
2226 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2229 pUrlEntry->dwUseCount--;
2230 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2232 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2237 /***********************************************************************
2238 * UnlockUrlCacheEntryFileW (WININET.@)
2241 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2243 LPURLCACHE_HEADER pHeader;
2244 struct _HASH_ENTRY * pHashEntry;
2245 CACHEFILE_ENTRY * pEntry;
2246 URL_CACHEFILE_ENTRY * pUrlEntry;
2247 URLCACHECONTAINER * pContainer;
2250 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2254 ERR("dwReserved != 0\n");
2255 SetLastError(ERROR_INVALID_PARAMETER);
2259 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2260 if (error != ERROR_SUCCESS)
2262 SetLastError(error);
2266 error = URLCacheContainer_OpenIndex(pContainer);
2267 if (error != ERROR_SUCCESS)
2269 SetLastError(error);
2273 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2276 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2278 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2279 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2280 SetLastError(ERROR_FILE_NOT_FOUND);
2284 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2285 if (pEntry->dwSignature != URL_SIGNATURE)
2287 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2288 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2289 SetLastError(ERROR_FILE_NOT_FOUND);
2293 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2295 if (pUrlEntry->dwUseCount == 0)
2297 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2300 pUrlEntry->dwUseCount--;
2301 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2303 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2308 /***********************************************************************
2309 * CreateUrlCacheEntryA (WININET.@)
2312 BOOL WINAPI CreateUrlCacheEntryA(
2313 IN LPCSTR lpszUrlName,
2314 IN DWORD dwExpectedFileSize,
2315 IN LPCSTR lpszFileExtension,
2316 OUT LPSTR lpszFileName,
2321 WCHAR *file_extension = NULL;
2322 WCHAR file_name[MAX_PATH];
2323 BOOL bSuccess = FALSE;
2326 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2327 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2329 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2331 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2333 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2335 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2341 dwError = GetLastError();
2346 dwError = GetLastError();
2348 HeapFree(GetProcessHeap(), 0, file_extension);
2352 dwError = GetLastError();
2354 HeapFree(GetProcessHeap(), 0, url_name);
2356 SetLastError(dwError);
2360 /***********************************************************************
2361 * CreateUrlCacheEntryW (WININET.@)
2364 BOOL WINAPI CreateUrlCacheEntryW(
2365 IN LPCWSTR lpszUrlName,
2366 IN DWORD dwExpectedFileSize,
2367 IN LPCWSTR lpszFileExtension,
2368 OUT LPWSTR lpszFileName,
2372 URLCACHECONTAINER * pContainer;
2373 LPURLCACHE_HEADER pHeader;
2374 CHAR szFile[MAX_PATH];
2375 WCHAR szExtension[MAX_PATH];
2376 LPCWSTR lpszUrlPart;
2378 LPCWSTR lpszFileNameExtension;
2379 LPWSTR lpszFileNameNoPath;
2381 int countnoextension;
2384 BOOL bFound = FALSE;
2390 static const WCHAR szWWW[] = {'w','w','w',0};
2391 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2393 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2394 debugstr_w(lpszUrlName),
2396 debugstr_w(lpszFileExtension),
2401 FIXME("dwReserved 0x%08x\n", dwReserved);
2403 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2405 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2408 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2410 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2412 lpszUrlEnd = lpszUrlPart;
2414 for (lpszUrlPart = lpszUrlEnd;
2415 (lpszUrlPart >= lpszUrlName);
2418 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2425 if (!lstrcmpW(lpszUrlPart, szWWW))
2427 lpszUrlPart += lstrlenW(szWWW);
2430 count = lpszUrlEnd - lpszUrlPart;
2432 if (bFound && (count < MAX_PATH))
2434 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2438 while(len && szFile[--len] == '/') szFile[len] = '\0';
2440 /* FIXME: get rid of illegal characters like \, / and : */
2444 FIXME("need to generate a random filename\n");
2447 TRACE("File name: %s\n", debugstr_a(szFile));
2449 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2450 if (error != ERROR_SUCCESS)
2452 SetLastError(error);
2456 error = URLCacheContainer_OpenIndex(pContainer);
2457 if (error != ERROR_SUCCESS)
2459 SetLastError(error);
2463 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2466 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2468 lBufferSize = MAX_PATH * sizeof(WCHAR);
2469 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2471 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2472 debugstr_a(szFile), lBufferSize);
2473 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2477 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2479 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2480 lpszFileNameNoPath >= lpszFileName;
2481 --lpszFileNameNoPath)
2483 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2487 countnoextension = lstrlenW(lpszFileNameNoPath);
2488 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2489 if (lpszFileNameExtension)
2490 countnoextension -= lstrlenW(lpszFileNameExtension);
2491 *szExtension = '\0';
2493 if (lpszFileExtension)
2495 szExtension[0] = '.';
2496 lstrcpyW(szExtension+1, lpszFileExtension);
2499 for (i = 0; i < 255; i++)
2501 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2504 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2505 for (p = lpszFileNameNoPath + 1; *p; p++)
2511 case '/': case '\\':
2518 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2520 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2521 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2522 if (hFile != INVALID_HANDLE_VALUE)
2529 GetSystemTimeAsFileTime(&ft);
2530 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2532 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2533 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2534 if (hFile != INVALID_HANDLE_VALUE)
2540 WARN("Could not find a unique filename\n");
2545 /***********************************************************************
2546 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2548 * The bug we are compensating for is that some drongo at Microsoft
2549 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2550 * As a consequence, CommitUrlCacheEntryA has been effectively
2551 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2552 * is still defined as LPCWSTR. The result (other than madness) is
2553 * that we always need to store lpHeaderInfo in CP_ACP rather than
2554 * in UTF16, and we need to avoid converting lpHeaderInfo in
2555 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2556 * result will lose data for arbitrary binary data.
2559 static BOOL CommitUrlCacheEntryInternal(
2560 IN LPCWSTR lpszUrlName,
2561 IN LPCWSTR lpszLocalFileName,
2562 IN FILETIME ExpireTime,
2563 IN FILETIME LastModifiedTime,
2564 IN DWORD CacheEntryType,
2565 IN LPBYTE lpHeaderInfo,
2566 IN DWORD dwHeaderSize,
2567 IN LPCWSTR lpszFileExtension,
2568 IN LPCWSTR lpszOriginalUrl
2571 URLCACHECONTAINER * pContainer;
2572 LPURLCACHE_HEADER pHeader;
2573 struct _HASH_ENTRY * pHashEntry;
2574 CACHEFILE_ENTRY * pEntry;
2575 URL_CACHEFILE_ENTRY * pUrlEntry;
2576 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2577 DWORD dwOffsetLocalFileName = 0;
2578 DWORD dwOffsetHeader = 0;
2579 DWORD dwOffsetFileExtension = 0;
2580 LARGE_INTEGER file_size;
2581 BYTE cDirectory = 0;
2582 char achFile[MAX_PATH];
2583 LPSTR lpszUrlNameA = NULL;
2584 LPSTR lpszFileExtensionA = NULL;
2585 char *pchLocalFileName = 0;
2588 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2589 debugstr_w(lpszUrlName),
2590 debugstr_w(lpszLocalFileName),
2594 debugstr_w(lpszFileExtension),
2595 debugstr_w(lpszOriginalUrl));
2597 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2599 SetLastError(ERROR_INVALID_PARAMETER);
2602 if (lpszOriginalUrl)
2603 WARN(": lpszOriginalUrl ignored\n");
2605 file_size.QuadPart = 0;
2606 if (lpszLocalFileName)
2610 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2611 if (hFile == INVALID_HANDLE_VALUE)
2613 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2618 if (!GetFileSizeEx(hFile, &file_size))
2620 ERR("couldn't get file size (error is %d)\n", GetLastError());
2628 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2629 if (error != ERROR_SUCCESS)
2631 SetLastError(error);
2635 error = URLCacheContainer_OpenIndex(pContainer);
2636 if (error != ERROR_SUCCESS)
2638 SetLastError(error);
2642 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2645 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2648 error = GetLastError();
2652 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2654 error = GetLastError();
2658 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2660 FIXME("entry already in cache - don't know what to do!\n");
2662 * SetLastError(ERROR_FILE_NOT_FOUND);
2668 if (lpszLocalFileName)
2670 BOOL bFound = FALSE;
2672 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2674 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2675 error = ERROR_INVALID_PARAMETER;
2679 /* skip container path prefix */
2680 lpszLocalFileName += lstrlenW(pContainer->path);
2682 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2683 pchLocalFileName = achFile;
2685 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2687 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2696 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2697 error = ERROR_INVALID_PARAMETER;
2701 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2704 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2705 if (lpszLocalFileName)
2707 dwOffsetLocalFileName = dwBytesNeeded;
2708 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2712 dwOffsetHeader = dwBytesNeeded;
2713 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2715 if (lpszFileExtensionA)
2717 dwOffsetFileExtension = dwBytesNeeded;
2718 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2721 /* round up to next block */
2722 if (dwBytesNeeded % BLOCKSIZE)
2724 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2725 dwBytesNeeded += BLOCKSIZE;
2728 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2730 ERR("no free entries\n");
2731 error = ERROR_DISK_FULL;
2735 /* FindFirstFreeEntry fills in blocks used */
2736 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2737 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2738 pUrlEntry->CacheDir = cDirectory;
2739 pUrlEntry->CacheEntryType = CacheEntryType;
2740 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2741 if (CacheEntryType & STICKY_CACHE_ENTRY)
2743 /* Sticky entries have a default exempt time of one day */
2744 pUrlEntry->dwExemptDelta = 86400;
2747 pUrlEntry->dwExemptDelta = 0;
2748 pUrlEntry->dwHitRate = 0;
2749 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2750 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2751 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2752 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2753 pUrlEntry->size.QuadPart = file_size.QuadPart;
2754 pUrlEntry->dwUseCount = 0;
2755 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2756 pUrlEntry->LastModifiedTime = LastModifiedTime;
2757 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2758 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2759 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2760 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2763 pUrlEntry->dwUnknown1 = 0;
2764 pUrlEntry->dwUnknown2 = 0;
2765 pUrlEntry->dwUnknown3 = 0x60;
2766 pUrlEntry->Unknown4 = 0;
2767 pUrlEntry->wUnknown5 = 0x1010;
2768 pUrlEntry->dwUnknown7 = 0;
2769 pUrlEntry->dwUnknown8 = 0;
2772 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2773 if (dwOffsetLocalFileName)
2774 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2776 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2777 if (dwOffsetFileExtension)
2778 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2780 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2781 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2782 if (error != ERROR_SUCCESS)
2783 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2786 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2787 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2788 if (CacheEntryType & STICKY_CACHE_ENTRY)
2789 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2791 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2792 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2793 pHeader->CacheLimit.QuadPart)
2794 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2798 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2799 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2800 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2802 if (error == ERROR_SUCCESS)
2806 SetLastError(error);
2811 /***********************************************************************
2812 * CommitUrlCacheEntryA (WININET.@)
2815 BOOL WINAPI CommitUrlCacheEntryA(
2816 IN LPCSTR lpszUrlName,
2817 IN LPCSTR lpszLocalFileName,
2818 IN FILETIME ExpireTime,
2819 IN FILETIME LastModifiedTime,
2820 IN DWORD CacheEntryType,
2821 IN LPBYTE lpHeaderInfo,
2822 IN DWORD dwHeaderSize,
2823 IN LPCSTR lpszFileExtension,
2824 IN LPCSTR lpszOriginalUrl
2827 WCHAR *url_name = NULL;
2828 WCHAR *local_file_name = NULL;
2829 WCHAR *original_url = NULL;
2830 WCHAR *file_extension = NULL;
2831 BOOL bSuccess = FALSE;
2833 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2834 debugstr_a(lpszUrlName),
2835 debugstr_a(lpszLocalFileName),
2839 debugstr_a(lpszFileExtension),
2840 debugstr_a(lpszOriginalUrl));
2842 url_name = heap_strdupAtoW(lpszUrlName);
2846 if (lpszLocalFileName)
2848 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2849 if (!local_file_name)
2852 if (lpszFileExtension)
2854 file_extension = heap_strdupAtoW(lpszFileExtension);
2855 if (!file_extension)
2858 if (lpszOriginalUrl)
2860 original_url = heap_strdupAtoW(lpszOriginalUrl);
2865 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2866 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2867 file_extension, original_url);
2870 HeapFree(GetProcessHeap(), 0, original_url);
2871 HeapFree(GetProcessHeap(), 0, file_extension);
2872 HeapFree(GetProcessHeap(), 0, local_file_name);
2873 HeapFree(GetProcessHeap(), 0, url_name);
2878 /***********************************************************************
2879 * CommitUrlCacheEntryW (WININET.@)
2882 BOOL WINAPI CommitUrlCacheEntryW(
2883 IN LPCWSTR lpszUrlName,
2884 IN LPCWSTR lpszLocalFileName,
2885 IN FILETIME ExpireTime,
2886 IN FILETIME LastModifiedTime,
2887 IN DWORD CacheEntryType,
2888 IN LPWSTR lpHeaderInfo,
2889 IN DWORD dwHeaderSize,
2890 IN LPCWSTR lpszFileExtension,
2891 IN LPCWSTR lpszOriginalUrl
2895 BOOL bSuccess = FALSE;
2897 CHAR *header_info = NULL;
2899 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2900 debugstr_w(lpszUrlName),
2901 debugstr_w(lpszLocalFileName),
2905 debugstr_w(lpszFileExtension),
2906 debugstr_w(lpszOriginalUrl));
2908 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2910 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2911 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2917 dwError = GetLastError();
2921 HeapFree(GetProcessHeap(), 0, header_info);
2923 SetLastError(dwError);
2929 /***********************************************************************
2930 * ReadUrlCacheEntryStream (WININET.@)
2933 BOOL WINAPI ReadUrlCacheEntryStream(
2934 IN HANDLE hUrlCacheStream,
2935 IN DWORD dwLocation,
2936 IN OUT LPVOID lpBuffer,
2937 IN OUT LPDWORD lpdwLen,
2941 /* Get handle to file from 'stream' */
2942 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2944 if (dwReserved != 0)
2946 ERR("dwReserved != 0\n");
2947 SetLastError(ERROR_INVALID_PARAMETER);
2951 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2953 SetLastError(ERROR_INVALID_HANDLE);
2957 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2959 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2962 /***********************************************************************
2963 * RetrieveUrlCacheEntryStreamA (WININET.@)
2966 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2967 IN LPCSTR lpszUrlName,
2968 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2969 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2970 IN BOOL fRandomRead,
2974 /* NOTE: this is not the same as the way that the native
2975 * version allocates 'stream' handles. I did it this way
2976 * as it is much easier and no applications should depend
2977 * on this behaviour. (Native version appears to allocate
2978 * indices into a table)
2980 STREAM_HANDLE * pStream;
2983 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2984 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2986 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2988 lpdwCacheEntryInfoBufferSize,
2994 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2999 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3001 if (hFile == INVALID_HANDLE_VALUE)
3004 /* allocate handle storage space */
3005 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3009 SetLastError(ERROR_OUTOFMEMORY);
3013 pStream->hFile = hFile;
3014 strcpy(pStream->lpszUrl, lpszUrlName);
3018 /***********************************************************************
3019 * RetrieveUrlCacheEntryStreamW (WININET.@)
3022 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3023 IN LPCWSTR lpszUrlName,
3024 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3025 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3026 IN BOOL fRandomRead,
3032 /* NOTE: this is not the same as the way that the native
3033 * version allocates 'stream' handles. I did it this way
3034 * as it is much easier and no applications should depend
3035 * on this behaviour. (Native version appears to allocate
3036 * indices into a table)
3038 STREAM_HANDLE * pStream;
3041 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3042 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3044 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3046 lpdwCacheEntryInfoBufferSize,
3052 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3057 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3059 if (hFile == INVALID_HANDLE_VALUE)
3062 /* allocate handle storage space */
3063 size = sizeof(STREAM_HANDLE);
3064 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3066 pStream = heap_alloc(size);
3070 SetLastError(ERROR_OUTOFMEMORY);
3074 pStream->hFile = hFile;
3075 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3079 /***********************************************************************
3080 * UnlockUrlCacheEntryStream (WININET.@)
3083 BOOL WINAPI UnlockUrlCacheEntryStream(
3084 IN HANDLE hUrlCacheStream,
3088 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3090 if (dwReserved != 0)
3092 ERR("dwReserved != 0\n");
3093 SetLastError(ERROR_INVALID_PARAMETER);
3097 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3099 SetLastError(ERROR_INVALID_HANDLE);
3103 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3106 /* close file handle */
3107 CloseHandle(pStream->hFile);
3109 /* free allocated space */
3110 HeapFree(GetProcessHeap(), 0, pStream);
3116 /***********************************************************************
3117 * DeleteUrlCacheEntryA (WININET.@)
3120 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3122 URLCACHECONTAINER * pContainer;
3123 LPURLCACHE_HEADER pHeader;
3124 struct _HASH_ENTRY * pHashEntry;
3128 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3130 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3131 if (error != ERROR_SUCCESS)
3133 SetLastError(error);
3137 error = URLCacheContainer_OpenIndex(pContainer);
3138 if (error != ERROR_SUCCESS)
3140 SetLastError(error);
3144 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3147 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3149 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3150 TRACE("entry %s not found!\n", lpszUrlName);
3151 SetLastError(ERROR_FILE_NOT_FOUND);
3155 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3157 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3162 /***********************************************************************
3163 * DeleteUrlCacheEntryW (WININET.@)
3166 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3168 URLCACHECONTAINER * pContainer;
3169 LPURLCACHE_HEADER pHeader;
3170 struct _HASH_ENTRY * pHashEntry;
3175 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3177 urlA = heap_strdupWtoA(lpszUrlName);
3180 SetLastError(ERROR_OUTOFMEMORY);
3184 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3185 if (error != ERROR_SUCCESS)
3187 HeapFree(GetProcessHeap(), 0, urlA);
3188 SetLastError(error);
3192 error = URLCacheContainer_OpenIndex(pContainer);
3193 if (error != ERROR_SUCCESS)
3195 HeapFree(GetProcessHeap(), 0, urlA);
3196 SetLastError(error);
3200 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3202 HeapFree(GetProcessHeap(), 0, urlA);
3206 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3208 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3209 TRACE("entry %s not found!\n", debugstr_a(urlA));
3210 HeapFree(GetProcessHeap(), 0, urlA);
3211 SetLastError(ERROR_FILE_NOT_FOUND);
3215 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3217 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3219 HeapFree(GetProcessHeap(), 0, urlA);
3223 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3225 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3229 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3231 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3235 /***********************************************************************
3236 * CreateCacheContainerA (WININET.@)
3238 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3239 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3241 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3242 d1, d2, d3, d4, d5, d6, d7, d8);
3246 /***********************************************************************
3247 * CreateCacheContainerW (WININET.@)
3249 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3250 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3252 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3253 d1, d2, d3, d4, d5, d6, d7, d8);
3257 /***********************************************************************
3258 * FindFirstUrlCacheContainerA (WININET.@)
3260 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3262 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3266 /***********************************************************************
3267 * FindFirstUrlCacheContainerW (WININET.@)
3269 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3271 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3275 /***********************************************************************
3276 * FindNextUrlCacheContainerA (WININET.@)
3278 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3280 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3284 /***********************************************************************
3285 * FindNextUrlCacheContainerW (WININET.@)
3287 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3289 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3293 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3294 LPCSTR lpszUrlSearchPattern,
3298 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3299 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3301 LPDWORD pcbReserved2,
3305 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3306 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3307 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3308 SetLastError(ERROR_FILE_NOT_FOUND);
3312 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3313 LPCWSTR lpszUrlSearchPattern,
3317 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3318 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3320 LPDWORD pcbReserved2,
3324 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3325 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3326 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3327 SetLastError(ERROR_FILE_NOT_FOUND);
3331 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3333 typedef struct URLCacheFindEntryHandle
3336 LPWSTR lpszUrlSearchPattern;
3337 DWORD dwContainerIndex;
3338 DWORD dwHashTableIndex;
3339 DWORD dwHashEntryIndex;
3340 } URLCacheFindEntryHandle;
3342 /***********************************************************************
3343 * FindFirstUrlCacheEntryA (WININET.@)
3346 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3347 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3349 URLCacheFindEntryHandle *pEntryHandle;
3351 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3353 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3357 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3358 if (lpszUrlSearchPattern)
3360 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3361 if (!pEntryHandle->lpszUrlSearchPattern)
3363 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3368 pEntryHandle->lpszUrlSearchPattern = NULL;
3369 pEntryHandle->dwContainerIndex = 0;
3370 pEntryHandle->dwHashTableIndex = 0;
3371 pEntryHandle->dwHashEntryIndex = 0;
3373 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3375 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3378 return pEntryHandle;
3381 /***********************************************************************
3382 * FindFirstUrlCacheEntryW (WININET.@)
3385 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3386 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3388 URLCacheFindEntryHandle *pEntryHandle;
3390 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3392 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3396 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3397 if (lpszUrlSearchPattern)
3399 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3400 if (!pEntryHandle->lpszUrlSearchPattern)
3402 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3407 pEntryHandle->lpszUrlSearchPattern = NULL;
3408 pEntryHandle->dwContainerIndex = 0;
3409 pEntryHandle->dwHashTableIndex = 0;
3410 pEntryHandle->dwHashEntryIndex = 0;
3412 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3414 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3417 return pEntryHandle;
3420 static BOOL FindNextUrlCacheEntryInternal(
3422 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3423 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3426 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3427 URLCACHECONTAINER * pContainer;
3429 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3431 SetLastError(ERROR_INVALID_HANDLE);
3435 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3436 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3438 LPURLCACHE_HEADER pHeader;
3439 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3442 error = URLCacheContainer_OpenIndex(pContainer);
3443 if (error != ERROR_SUCCESS)
3445 SetLastError(error);
3449 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3452 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3453 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3455 const struct _HASH_ENTRY *pHashEntry = NULL;
3456 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3457 pEntryHandle->dwHashEntryIndex++)
3459 const URL_CACHEFILE_ENTRY *pUrlEntry;
3460 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3462 if (pEntry->dwSignature != URL_SIGNATURE)
3465 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3466 TRACE("Found URL: %s\n",
3467 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3468 TRACE("Header info: %s\n",
3469 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3471 error = URLCache_CopyEntry(
3474 lpNextCacheEntryInfo,
3475 lpdwNextCacheEntryInfoBufferSize,
3478 if (error != ERROR_SUCCESS)
3480 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3481 SetLastError(error);
3484 TRACE("Local File Name: %s\n",
3485 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3487 /* increment the current index so that next time the function
3488 * is called the next entry is returned */
3489 pEntryHandle->dwHashEntryIndex++;
3490 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3495 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3498 SetLastError(ERROR_NO_MORE_ITEMS);
3502 /***********************************************************************
3503 * FindNextUrlCacheEntryA (WININET.@)
3505 BOOL WINAPI FindNextUrlCacheEntryA(
3507 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3508 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3510 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3512 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3513 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3516 /***********************************************************************
3517 * FindNextUrlCacheEntryW (WININET.@)
3519 BOOL WINAPI FindNextUrlCacheEntryW(
3521 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3522 LPDWORD lpdwNextCacheEntryInfoBufferSize
3525 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3527 return FindNextUrlCacheEntryInternal(hEnumHandle,
3528 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3529 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3532 /***********************************************************************
3533 * FindCloseUrlCache (WININET.@)
3535 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3537 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3539 TRACE("(%p)\n", hEnumHandle);
3541 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3543 SetLastError(ERROR_INVALID_HANDLE);
3547 pEntryHandle->dwMagic = 0;
3548 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3549 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3554 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3555 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3557 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3558 dwSearchCondition, lpGroupId, lpReserved);
3562 BOOL WINAPI FindNextUrlCacheEntryExA(
3564 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3565 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3567 LPDWORD pcbReserved2,
3571 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3572 lpReserved, pcbReserved2, lpReserved3);
3576 BOOL WINAPI FindNextUrlCacheEntryExW(
3578 LPINTERNET_CACHE_ENTRY_INFOW 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 FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3592 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3596 /***********************************************************************
3597 * CreateUrlCacheGroup (WININET.@)
3600 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3602 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3606 /***********************************************************************
3607 * DeleteUrlCacheGroup (WININET.@)
3610 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3612 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3613 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3617 /***********************************************************************
3618 * SetUrlCacheEntryGroupA (WININET.@)
3621 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3622 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3625 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3626 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3627 pbGroupAttributes, cbGroupAttributes, lpReserved);
3628 SetLastError(ERROR_FILE_NOT_FOUND);
3632 /***********************************************************************
3633 * SetUrlCacheEntryGroupW (WININET.@)
3636 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3637 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3640 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3641 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3642 pbGroupAttributes, cbGroupAttributes, lpReserved);
3643 SetLastError(ERROR_FILE_NOT_FOUND);
3647 /***********************************************************************
3648 * GetUrlCacheConfigInfoW (WININET.@)
3650 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3652 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3653 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3657 /***********************************************************************
3658 * GetUrlCacheConfigInfoA (WININET.@)
3660 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3662 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3663 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3667 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3668 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3669 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3671 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3672 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3673 lpdwGroupInfo, lpReserved);
3677 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3678 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3679 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3681 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3682 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3683 lpdwGroupInfo, lpReserved);
3687 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3688 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3690 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3691 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3695 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3696 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3698 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3699 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3703 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3705 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3709 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3711 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3715 /***********************************************************************
3716 * DeleteIE3Cache (WININET.@)
3718 * Deletes the files used by the IE3 URL caching system.
3721 * hWnd [I] A dummy window.
3722 * hInst [I] Instance of process calling the function.
3723 * lpszCmdLine [I] Options used by function.
3724 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3726 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3728 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3732 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3733 FILETIME *pftLastModified)
3736 FILETIME now, expired;
3738 *pftLastModified = pUrlEntry->LastModifiedTime;
3739 GetSystemTimeAsFileTime(&now);
3740 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3741 pUrlEntry->wExpiredTime, &expired);
3742 /* If the expired time is 0, it's interpreted as not expired */
3743 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3746 ret = CompareFileTime(&expired, &now) < 0;
3750 /***********************************************************************
3751 * IsUrlCacheEntryExpiredA (WININET.@)
3755 * dwFlags [I] Unknown
3756 * pftLastModified [O] Last modified time
3758 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3760 LPURLCACHE_HEADER pHeader;
3761 struct _HASH_ENTRY * pHashEntry;
3762 const CACHEFILE_ENTRY * pEntry;
3763 const URL_CACHEFILE_ENTRY * pUrlEntry;
3764 URLCACHECONTAINER * pContainer;
3767 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3769 if (!url || !pftLastModified)
3772 FIXME("unknown flags 0x%08x\n", dwFlags);
3774 /* Any error implies that the URL is expired, i.e. not in the cache */
3775 if (URLCacheContainers_FindContainerA(url, &pContainer))
3777 memset(pftLastModified, 0, sizeof(*pftLastModified));
3781 if (URLCacheContainer_OpenIndex(pContainer))
3783 memset(pftLastModified, 0, sizeof(*pftLastModified));
3787 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3789 memset(pftLastModified, 0, sizeof(*pftLastModified));
3793 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3795 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3796 memset(pftLastModified, 0, sizeof(*pftLastModified));
3797 TRACE("entry %s not found!\n", url);
3801 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3802 if (pEntry->dwSignature != URL_SIGNATURE)
3804 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3805 memset(pftLastModified, 0, sizeof(*pftLastModified));
3806 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3810 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3811 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3813 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3818 /***********************************************************************
3819 * IsUrlCacheEntryExpiredW (WININET.@)
3823 * dwFlags [I] Unknown
3824 * pftLastModified [O] Last modified time
3826 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3828 LPURLCACHE_HEADER pHeader;
3829 struct _HASH_ENTRY * pHashEntry;
3830 const CACHEFILE_ENTRY * pEntry;
3831 const URL_CACHEFILE_ENTRY * pUrlEntry;
3832 URLCACHECONTAINER * pContainer;
3835 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3837 if (!url || !pftLastModified)
3840 FIXME("unknown flags 0x%08x\n", dwFlags);
3842 /* Any error implies that the URL is expired, i.e. not in the cache */
3843 if (URLCacheContainers_FindContainerW(url, &pContainer))
3845 memset(pftLastModified, 0, sizeof(*pftLastModified));
3849 if (URLCacheContainer_OpenIndex(pContainer))
3851 memset(pftLastModified, 0, sizeof(*pftLastModified));
3855 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3857 memset(pftLastModified, 0, sizeof(*pftLastModified));
3861 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3863 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3864 memset(pftLastModified, 0, sizeof(*pftLastModified));
3865 TRACE("entry %s not found!\n", debugstr_w(url));
3869 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3871 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3872 memset(pftLastModified, 0, sizeof(*pftLastModified));
3873 TRACE("entry %s not found!\n", debugstr_w(url));
3877 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3878 if (pEntry->dwSignature != URL_SIGNATURE)
3880 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3881 memset(pftLastModified, 0, sizeof(*pftLastModified));
3882 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3886 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3887 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3889 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3894 /***********************************************************************
3895 * GetDiskInfoA (WININET.@)
3897 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3900 ULARGE_INTEGER bytes_free, bytes_total;
3902 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3906 SetLastError(ERROR_INVALID_PARAMETER);
3910 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3912 if (cluster_size) *cluster_size = 1;
3913 if (free) *free = bytes_free.QuadPart;
3914 if (total) *total = bytes_total.QuadPart;
3919 /***********************************************************************
3920 * RegisterUrlCacheNotification (WININET.@)
3922 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3924 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3928 /***********************************************************************
3929 * IncrementUrlCacheHeaderData (WININET.@)
3931 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3933 FIXME("(%u, %p)\n", index, data);