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 heap_free(pContainer);
476 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
477 if (!pContainer->cache_prefix)
479 heap_free(pContainer->path);
480 heap_free(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 heap_free(pContainer->path);
493 heap_free(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 heap_free(pContainer->path);
509 heap_free(pContainer->cache_prefix);
510 heap_free(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);
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);
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 heap_free(file_extension);
2352 dwError = GetLastError();
2354 heap_free(url_name);
2355 if (!bSuccess) SetLastError(dwError);
2359 /***********************************************************************
2360 * CreateUrlCacheEntryW (WININET.@)
2363 BOOL WINAPI CreateUrlCacheEntryW(
2364 IN LPCWSTR lpszUrlName,
2365 IN DWORD dwExpectedFileSize,
2366 IN LPCWSTR lpszFileExtension,
2367 OUT LPWSTR lpszFileName,
2371 URLCACHECONTAINER * pContainer;
2372 LPURLCACHE_HEADER pHeader;
2373 CHAR szFile[MAX_PATH];
2374 WCHAR szExtension[MAX_PATH];
2375 LPCWSTR lpszUrlPart;
2377 LPCWSTR lpszFileNameExtension;
2378 LPWSTR lpszFileNameNoPath;
2380 int countnoextension;
2383 BOOL bFound = FALSE;
2389 static const WCHAR szWWW[] = {'w','w','w',0};
2390 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2392 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2393 debugstr_w(lpszUrlName),
2395 debugstr_w(lpszFileExtension),
2400 FIXME("dwReserved 0x%08x\n", dwReserved);
2402 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2404 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2407 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2409 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2411 lpszUrlEnd = lpszUrlPart;
2413 for (lpszUrlPart = lpszUrlEnd;
2414 (lpszUrlPart >= lpszUrlName);
2417 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2424 if (!lstrcmpW(lpszUrlPart, szWWW))
2426 lpszUrlPart += lstrlenW(szWWW);
2429 count = lpszUrlEnd - lpszUrlPart;
2431 if (bFound && (count < MAX_PATH))
2433 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2437 while(len && szFile[--len] == '/') szFile[len] = '\0';
2439 /* FIXME: get rid of illegal characters like \, / and : */
2443 FIXME("need to generate a random filename\n");
2446 TRACE("File name: %s\n", debugstr_a(szFile));
2448 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2449 if (error != ERROR_SUCCESS)
2451 SetLastError(error);
2455 error = URLCacheContainer_OpenIndex(pContainer);
2456 if (error != ERROR_SUCCESS)
2458 SetLastError(error);
2462 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2465 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2467 lBufferSize = MAX_PATH * sizeof(WCHAR);
2468 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2470 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2471 debugstr_a(szFile), lBufferSize);
2472 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2476 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2478 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2479 lpszFileNameNoPath >= lpszFileName;
2480 --lpszFileNameNoPath)
2482 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2486 countnoextension = lstrlenW(lpszFileNameNoPath);
2487 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2488 if (lpszFileNameExtension)
2489 countnoextension -= lstrlenW(lpszFileNameExtension);
2490 *szExtension = '\0';
2492 if (lpszFileExtension)
2494 szExtension[0] = '.';
2495 lstrcpyW(szExtension+1, lpszFileExtension);
2498 for (i = 0; i < 255; i++)
2500 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2503 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2504 for (p = lpszFileNameNoPath + 1; *p; p++)
2510 case '/': case '\\':
2517 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2519 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2520 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2521 if (hFile != INVALID_HANDLE_VALUE)
2528 GetSystemTimeAsFileTime(&ft);
2529 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2531 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2532 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2533 if (hFile != INVALID_HANDLE_VALUE)
2539 WARN("Could not find a unique filename\n");
2544 /***********************************************************************
2545 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2547 * The bug we are compensating for is that some drongo at Microsoft
2548 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2549 * As a consequence, CommitUrlCacheEntryA has been effectively
2550 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2551 * is still defined as LPCWSTR. The result (other than madness) is
2552 * that we always need to store lpHeaderInfo in CP_ACP rather than
2553 * in UTF16, and we need to avoid converting lpHeaderInfo in
2554 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2555 * result will lose data for arbitrary binary data.
2558 static BOOL CommitUrlCacheEntryInternal(
2559 IN LPCWSTR lpszUrlName,
2560 IN LPCWSTR lpszLocalFileName,
2561 IN FILETIME ExpireTime,
2562 IN FILETIME LastModifiedTime,
2563 IN DWORD CacheEntryType,
2564 IN LPBYTE lpHeaderInfo,
2565 IN DWORD dwHeaderSize,
2566 IN LPCWSTR lpszFileExtension,
2567 IN LPCWSTR lpszOriginalUrl
2570 URLCACHECONTAINER * pContainer;
2571 LPURLCACHE_HEADER pHeader;
2572 struct _HASH_ENTRY * pHashEntry;
2573 CACHEFILE_ENTRY * pEntry;
2574 URL_CACHEFILE_ENTRY * pUrlEntry;
2575 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2576 DWORD dwOffsetLocalFileName = 0;
2577 DWORD dwOffsetHeader = 0;
2578 DWORD dwOffsetFileExtension = 0;
2579 LARGE_INTEGER file_size;
2580 BYTE cDirectory = 0;
2581 char achFile[MAX_PATH];
2582 LPSTR lpszUrlNameA = NULL;
2583 LPSTR lpszFileExtensionA = NULL;
2584 char *pchLocalFileName = 0;
2587 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2588 debugstr_w(lpszUrlName),
2589 debugstr_w(lpszLocalFileName),
2593 debugstr_w(lpszFileExtension),
2594 debugstr_w(lpszOriginalUrl));
2596 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2598 SetLastError(ERROR_INVALID_PARAMETER);
2601 if (lpszOriginalUrl)
2602 WARN(": lpszOriginalUrl ignored\n");
2604 file_size.QuadPart = 0;
2605 if (lpszLocalFileName)
2609 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2610 if (hFile == INVALID_HANDLE_VALUE)
2612 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2617 if (!GetFileSizeEx(hFile, &file_size))
2619 ERR("couldn't get file size (error is %d)\n", GetLastError());
2627 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2628 if (error != ERROR_SUCCESS)
2630 SetLastError(error);
2634 error = URLCacheContainer_OpenIndex(pContainer);
2635 if (error != ERROR_SUCCESS)
2637 SetLastError(error);
2641 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2644 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2647 error = GetLastError();
2651 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2653 error = GetLastError();
2657 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2659 FIXME("entry already in cache - don't know what to do!\n");
2661 * SetLastError(ERROR_FILE_NOT_FOUND);
2667 if (lpszLocalFileName)
2669 BOOL bFound = FALSE;
2671 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2673 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2674 error = ERROR_INVALID_PARAMETER;
2678 /* skip container path prefix */
2679 lpszLocalFileName += lstrlenW(pContainer->path);
2681 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2682 pchLocalFileName = achFile;
2684 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2686 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2695 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2696 error = ERROR_INVALID_PARAMETER;
2700 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2703 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2704 if (lpszLocalFileName)
2706 dwOffsetLocalFileName = dwBytesNeeded;
2707 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2711 dwOffsetHeader = dwBytesNeeded;
2712 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2714 if (lpszFileExtensionA)
2716 dwOffsetFileExtension = dwBytesNeeded;
2717 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2720 /* round up to next block */
2721 if (dwBytesNeeded % BLOCKSIZE)
2723 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2724 dwBytesNeeded += BLOCKSIZE;
2727 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2729 ERR("no free entries\n");
2730 error = ERROR_DISK_FULL;
2734 /* FindFirstFreeEntry fills in blocks used */
2735 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2736 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2737 pUrlEntry->CacheDir = cDirectory;
2738 pUrlEntry->CacheEntryType = CacheEntryType;
2739 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2740 if (CacheEntryType & STICKY_CACHE_ENTRY)
2742 /* Sticky entries have a default exempt time of one day */
2743 pUrlEntry->dwExemptDelta = 86400;
2746 pUrlEntry->dwExemptDelta = 0;
2747 pUrlEntry->dwHitRate = 0;
2748 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2749 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2750 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2751 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2752 pUrlEntry->size.QuadPart = file_size.QuadPart;
2753 pUrlEntry->dwUseCount = 0;
2754 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2755 pUrlEntry->LastModifiedTime = LastModifiedTime;
2756 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2757 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2758 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2759 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2762 pUrlEntry->dwUnknown1 = 0;
2763 pUrlEntry->dwUnknown2 = 0;
2764 pUrlEntry->dwUnknown3 = 0x60;
2765 pUrlEntry->Unknown4 = 0;
2766 pUrlEntry->wUnknown5 = 0x1010;
2767 pUrlEntry->dwUnknown7 = 0;
2768 pUrlEntry->dwUnknown8 = 0;
2771 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2772 if (dwOffsetLocalFileName)
2773 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2775 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2776 if (dwOffsetFileExtension)
2777 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2779 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2780 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2781 if (error != ERROR_SUCCESS)
2782 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2785 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2786 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2787 if (CacheEntryType & STICKY_CACHE_ENTRY)
2788 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2790 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2791 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2792 pHeader->CacheLimit.QuadPart)
2793 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2797 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2798 heap_free(lpszUrlNameA);
2799 heap_free(lpszFileExtensionA);
2801 if (error == ERROR_SUCCESS)
2805 SetLastError(error);
2810 /***********************************************************************
2811 * CommitUrlCacheEntryA (WININET.@)
2814 BOOL WINAPI CommitUrlCacheEntryA(
2815 IN LPCSTR lpszUrlName,
2816 IN LPCSTR lpszLocalFileName,
2817 IN FILETIME ExpireTime,
2818 IN FILETIME LastModifiedTime,
2819 IN DWORD CacheEntryType,
2820 IN LPBYTE lpHeaderInfo,
2821 IN DWORD dwHeaderSize,
2822 IN LPCSTR lpszFileExtension,
2823 IN LPCSTR lpszOriginalUrl
2826 WCHAR *url_name = NULL;
2827 WCHAR *local_file_name = NULL;
2828 WCHAR *original_url = NULL;
2829 WCHAR *file_extension = NULL;
2830 BOOL bSuccess = FALSE;
2832 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2833 debugstr_a(lpszUrlName),
2834 debugstr_a(lpszLocalFileName),
2838 debugstr_a(lpszFileExtension),
2839 debugstr_a(lpszOriginalUrl));
2841 url_name = heap_strdupAtoW(lpszUrlName);
2845 if (lpszLocalFileName)
2847 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2848 if (!local_file_name)
2851 if (lpszFileExtension)
2853 file_extension = heap_strdupAtoW(lpszFileExtension);
2854 if (!file_extension)
2857 if (lpszOriginalUrl)
2859 original_url = heap_strdupAtoW(lpszOriginalUrl);
2864 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2865 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2866 file_extension, original_url);
2869 heap_free(original_url);
2870 heap_free(file_extension);
2871 heap_free(local_file_name);
2872 heap_free(url_name);
2876 /***********************************************************************
2877 * CommitUrlCacheEntryW (WININET.@)
2880 BOOL WINAPI CommitUrlCacheEntryW(
2881 IN LPCWSTR lpszUrlName,
2882 IN LPCWSTR lpszLocalFileName,
2883 IN FILETIME ExpireTime,
2884 IN FILETIME LastModifiedTime,
2885 IN DWORD CacheEntryType,
2886 IN LPWSTR lpHeaderInfo,
2887 IN DWORD dwHeaderSize,
2888 IN LPCWSTR lpszFileExtension,
2889 IN LPCWSTR lpszOriginalUrl
2893 BOOL bSuccess = FALSE;
2895 CHAR *header_info = NULL;
2897 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2898 debugstr_w(lpszUrlName),
2899 debugstr_w(lpszLocalFileName),
2903 debugstr_w(lpszFileExtension),
2904 debugstr_w(lpszOriginalUrl));
2906 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2908 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2909 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2915 dwError = GetLastError();
2919 heap_free(header_info);
2921 SetLastError(dwError);
2927 /***********************************************************************
2928 * ReadUrlCacheEntryStream (WININET.@)
2931 BOOL WINAPI ReadUrlCacheEntryStream(
2932 IN HANDLE hUrlCacheStream,
2933 IN DWORD dwLocation,
2934 IN OUT LPVOID lpBuffer,
2935 IN OUT LPDWORD lpdwLen,
2939 /* Get handle to file from 'stream' */
2940 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2942 if (dwReserved != 0)
2944 ERR("dwReserved != 0\n");
2945 SetLastError(ERROR_INVALID_PARAMETER);
2949 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2951 SetLastError(ERROR_INVALID_HANDLE);
2955 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2957 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2960 /***********************************************************************
2961 * RetrieveUrlCacheEntryStreamA (WININET.@)
2964 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2965 IN LPCSTR lpszUrlName,
2966 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2967 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2968 IN BOOL fRandomRead,
2972 /* NOTE: this is not the same as the way that the native
2973 * version allocates 'stream' handles. I did it this way
2974 * as it is much easier and no applications should depend
2975 * on this behaviour. (Native version appears to allocate
2976 * indices into a table)
2978 STREAM_HANDLE * pStream;
2981 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2982 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2984 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2986 lpdwCacheEntryInfoBufferSize,
2992 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2997 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2999 if (hFile == INVALID_HANDLE_VALUE)
3002 /* allocate handle storage space */
3003 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3007 SetLastError(ERROR_OUTOFMEMORY);
3011 pStream->hFile = hFile;
3012 strcpy(pStream->lpszUrl, lpszUrlName);
3016 /***********************************************************************
3017 * RetrieveUrlCacheEntryStreamW (WININET.@)
3020 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3021 IN LPCWSTR lpszUrlName,
3022 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3023 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3024 IN BOOL fRandomRead,
3030 /* NOTE: this is not the same as the way that the native
3031 * version allocates 'stream' handles. I did it this way
3032 * as it is much easier and no applications should depend
3033 * on this behaviour. (Native version appears to allocate
3034 * indices into a table)
3036 STREAM_HANDLE * pStream;
3039 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3040 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3042 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3044 lpdwCacheEntryInfoBufferSize,
3050 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3055 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3057 if (hFile == INVALID_HANDLE_VALUE)
3060 /* allocate handle storage space */
3061 size = sizeof(STREAM_HANDLE);
3062 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3064 pStream = heap_alloc(size);
3068 SetLastError(ERROR_OUTOFMEMORY);
3072 pStream->hFile = hFile;
3073 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3077 /***********************************************************************
3078 * UnlockUrlCacheEntryStream (WININET.@)
3081 BOOL WINAPI UnlockUrlCacheEntryStream(
3082 IN HANDLE hUrlCacheStream,
3086 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3088 if (dwReserved != 0)
3090 ERR("dwReserved != 0\n");
3091 SetLastError(ERROR_INVALID_PARAMETER);
3095 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3097 SetLastError(ERROR_INVALID_HANDLE);
3101 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3104 CloseHandle(pStream->hFile);
3110 /***********************************************************************
3111 * DeleteUrlCacheEntryA (WININET.@)
3114 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3116 URLCACHECONTAINER * pContainer;
3117 LPURLCACHE_HEADER pHeader;
3118 struct _HASH_ENTRY * pHashEntry;
3122 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3124 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3125 if (error != ERROR_SUCCESS)
3127 SetLastError(error);
3131 error = URLCacheContainer_OpenIndex(pContainer);
3132 if (error != ERROR_SUCCESS)
3134 SetLastError(error);
3138 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3141 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3143 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3144 TRACE("entry %s not found!\n", lpszUrlName);
3145 SetLastError(ERROR_FILE_NOT_FOUND);
3149 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3151 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3156 /***********************************************************************
3157 * DeleteUrlCacheEntryW (WININET.@)
3160 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3162 URLCACHECONTAINER * pContainer;
3163 LPURLCACHE_HEADER pHeader;
3164 struct _HASH_ENTRY * pHashEntry;
3169 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3171 urlA = heap_strdupWtoA(lpszUrlName);
3174 SetLastError(ERROR_OUTOFMEMORY);
3178 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3179 if (error != ERROR_SUCCESS)
3182 SetLastError(error);
3186 error = URLCacheContainer_OpenIndex(pContainer);
3187 if (error != ERROR_SUCCESS)
3190 SetLastError(error);
3194 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3200 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3202 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3203 TRACE("entry %s not found!\n", debugstr_a(urlA));
3205 SetLastError(ERROR_FILE_NOT_FOUND);
3209 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3211 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3216 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3218 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3222 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3224 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3228 /***********************************************************************
3229 * CreateCacheContainerA (WININET.@)
3231 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3232 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3234 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3235 d1, d2, d3, d4, d5, d6, d7, d8);
3239 /***********************************************************************
3240 * CreateCacheContainerW (WININET.@)
3242 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3243 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3245 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3246 d1, d2, d3, d4, d5, d6, d7, d8);
3250 /***********************************************************************
3251 * FindFirstUrlCacheContainerA (WININET.@)
3253 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3255 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3259 /***********************************************************************
3260 * FindFirstUrlCacheContainerW (WININET.@)
3262 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3264 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3268 /***********************************************************************
3269 * FindNextUrlCacheContainerA (WININET.@)
3271 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3273 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3277 /***********************************************************************
3278 * FindNextUrlCacheContainerW (WININET.@)
3280 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3282 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3286 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3287 LPCSTR lpszUrlSearchPattern,
3291 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3292 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3294 LPDWORD pcbReserved2,
3298 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3299 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3300 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3301 SetLastError(ERROR_FILE_NOT_FOUND);
3305 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3306 LPCWSTR lpszUrlSearchPattern,
3310 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3311 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3313 LPDWORD pcbReserved2,
3317 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3318 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3319 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3320 SetLastError(ERROR_FILE_NOT_FOUND);
3324 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3326 typedef struct URLCacheFindEntryHandle
3329 LPWSTR lpszUrlSearchPattern;
3330 DWORD dwContainerIndex;
3331 DWORD dwHashTableIndex;
3332 DWORD dwHashEntryIndex;
3333 } URLCacheFindEntryHandle;
3335 /***********************************************************************
3336 * FindFirstUrlCacheEntryA (WININET.@)
3339 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3340 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3342 URLCacheFindEntryHandle *pEntryHandle;
3344 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3346 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3350 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3351 if (lpszUrlSearchPattern)
3353 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3354 if (!pEntryHandle->lpszUrlSearchPattern)
3356 heap_free(pEntryHandle);
3361 pEntryHandle->lpszUrlSearchPattern = NULL;
3362 pEntryHandle->dwContainerIndex = 0;
3363 pEntryHandle->dwHashTableIndex = 0;
3364 pEntryHandle->dwHashEntryIndex = 0;
3366 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3368 heap_free(pEntryHandle);
3371 return pEntryHandle;
3374 /***********************************************************************
3375 * FindFirstUrlCacheEntryW (WININET.@)
3378 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3379 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3381 URLCacheFindEntryHandle *pEntryHandle;
3383 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3385 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3389 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3390 if (lpszUrlSearchPattern)
3392 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3393 if (!pEntryHandle->lpszUrlSearchPattern)
3395 heap_free(pEntryHandle);
3400 pEntryHandle->lpszUrlSearchPattern = NULL;
3401 pEntryHandle->dwContainerIndex = 0;
3402 pEntryHandle->dwHashTableIndex = 0;
3403 pEntryHandle->dwHashEntryIndex = 0;
3405 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3407 heap_free(pEntryHandle);
3410 return pEntryHandle;
3413 static BOOL FindNextUrlCacheEntryInternal(
3415 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3416 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3419 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3420 URLCACHECONTAINER * pContainer;
3422 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3424 SetLastError(ERROR_INVALID_HANDLE);
3428 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3429 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3431 LPURLCACHE_HEADER pHeader;
3432 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3435 error = URLCacheContainer_OpenIndex(pContainer);
3436 if (error != ERROR_SUCCESS)
3438 SetLastError(error);
3442 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3445 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3446 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3448 const struct _HASH_ENTRY *pHashEntry = NULL;
3449 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3450 pEntryHandle->dwHashEntryIndex++)
3452 const URL_CACHEFILE_ENTRY *pUrlEntry;
3453 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3455 if (pEntry->dwSignature != URL_SIGNATURE)
3458 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3459 TRACE("Found URL: %s\n",
3460 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3461 TRACE("Header info: %s\n",
3462 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3464 error = URLCache_CopyEntry(
3467 lpNextCacheEntryInfo,
3468 lpdwNextCacheEntryInfoBufferSize,
3471 if (error != ERROR_SUCCESS)
3473 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3474 SetLastError(error);
3477 TRACE("Local File Name: %s\n",
3478 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3480 /* increment the current index so that next time the function
3481 * is called the next entry is returned */
3482 pEntryHandle->dwHashEntryIndex++;
3483 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3488 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3491 SetLastError(ERROR_NO_MORE_ITEMS);
3495 /***********************************************************************
3496 * FindNextUrlCacheEntryA (WININET.@)
3498 BOOL WINAPI FindNextUrlCacheEntryA(
3500 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3501 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3503 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3505 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3506 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3509 /***********************************************************************
3510 * FindNextUrlCacheEntryW (WININET.@)
3512 BOOL WINAPI FindNextUrlCacheEntryW(
3514 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3515 LPDWORD lpdwNextCacheEntryInfoBufferSize
3518 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3520 return FindNextUrlCacheEntryInternal(hEnumHandle,
3521 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3522 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3525 /***********************************************************************
3526 * FindCloseUrlCache (WININET.@)
3528 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3530 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3532 TRACE("(%p)\n", hEnumHandle);
3534 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3536 SetLastError(ERROR_INVALID_HANDLE);
3540 pEntryHandle->dwMagic = 0;
3541 heap_free(pEntryHandle->lpszUrlSearchPattern);
3542 heap_free(pEntryHandle);
3546 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3547 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3549 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3550 dwSearchCondition, lpGroupId, lpReserved);
3554 BOOL WINAPI FindNextUrlCacheEntryExA(
3556 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3557 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3559 LPDWORD pcbReserved2,
3563 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3564 lpReserved, pcbReserved2, lpReserved3);
3568 BOOL WINAPI FindNextUrlCacheEntryExW(
3570 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3571 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3573 LPDWORD pcbReserved2,
3577 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3578 lpReserved, pcbReserved2, lpReserved3);
3582 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3584 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3588 /***********************************************************************
3589 * CreateUrlCacheGroup (WININET.@)
3592 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3594 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3598 /***********************************************************************
3599 * DeleteUrlCacheGroup (WININET.@)
3602 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3604 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3605 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3609 /***********************************************************************
3610 * SetUrlCacheEntryGroupA (WININET.@)
3613 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3614 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3617 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3618 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3619 pbGroupAttributes, cbGroupAttributes, lpReserved);
3620 SetLastError(ERROR_FILE_NOT_FOUND);
3624 /***********************************************************************
3625 * SetUrlCacheEntryGroupW (WININET.@)
3628 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3629 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3632 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3633 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3634 pbGroupAttributes, cbGroupAttributes, lpReserved);
3635 SetLastError(ERROR_FILE_NOT_FOUND);
3639 /***********************************************************************
3640 * GetUrlCacheConfigInfoW (WININET.@)
3642 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3644 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3645 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3649 /***********************************************************************
3650 * GetUrlCacheConfigInfoA (WININET.@)
3652 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3654 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3655 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3659 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3660 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3661 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3663 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3664 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3665 lpdwGroupInfo, lpReserved);
3669 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3670 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3671 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3673 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3674 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3675 lpdwGroupInfo, lpReserved);
3679 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3680 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3682 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3683 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3687 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3688 LPINTERNET_CACHE_GROUP_INFOW 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 SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3697 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3701 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3703 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3707 /***********************************************************************
3708 * DeleteIE3Cache (WININET.@)
3710 * Deletes the files used by the IE3 URL caching system.
3713 * hWnd [I] A dummy window.
3714 * hInst [I] Instance of process calling the function.
3715 * lpszCmdLine [I] Options used by function.
3716 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3718 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3720 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3724 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3725 FILETIME *pftLastModified)
3728 FILETIME now, expired;
3730 *pftLastModified = pUrlEntry->LastModifiedTime;
3731 GetSystemTimeAsFileTime(&now);
3732 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3733 pUrlEntry->wExpiredTime, &expired);
3734 /* If the expired time is 0, it's interpreted as not expired */
3735 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3738 ret = CompareFileTime(&expired, &now) < 0;
3742 /***********************************************************************
3743 * IsUrlCacheEntryExpiredA (WININET.@)
3747 * dwFlags [I] Unknown
3748 * pftLastModified [O] Last modified time
3750 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3752 LPURLCACHE_HEADER pHeader;
3753 struct _HASH_ENTRY * pHashEntry;
3754 const CACHEFILE_ENTRY * pEntry;
3755 const URL_CACHEFILE_ENTRY * pUrlEntry;
3756 URLCACHECONTAINER * pContainer;
3759 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3761 if (!url || !pftLastModified)
3764 FIXME("unknown flags 0x%08x\n", dwFlags);
3766 /* Any error implies that the URL is expired, i.e. not in the cache */
3767 if (URLCacheContainers_FindContainerA(url, &pContainer))
3769 memset(pftLastModified, 0, sizeof(*pftLastModified));
3773 if (URLCacheContainer_OpenIndex(pContainer))
3775 memset(pftLastModified, 0, sizeof(*pftLastModified));
3779 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3781 memset(pftLastModified, 0, sizeof(*pftLastModified));
3785 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3787 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3788 memset(pftLastModified, 0, sizeof(*pftLastModified));
3789 TRACE("entry %s not found!\n", url);
3793 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3794 if (pEntry->dwSignature != URL_SIGNATURE)
3796 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3797 memset(pftLastModified, 0, sizeof(*pftLastModified));
3798 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3802 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3803 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3805 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3810 /***********************************************************************
3811 * IsUrlCacheEntryExpiredW (WININET.@)
3815 * dwFlags [I] Unknown
3816 * pftLastModified [O] Last modified time
3818 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3820 LPURLCACHE_HEADER pHeader;
3821 struct _HASH_ENTRY * pHashEntry;
3822 const CACHEFILE_ENTRY * pEntry;
3823 const URL_CACHEFILE_ENTRY * pUrlEntry;
3824 URLCACHECONTAINER * pContainer;
3827 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3829 if (!url || !pftLastModified)
3832 FIXME("unknown flags 0x%08x\n", dwFlags);
3834 /* Any error implies that the URL is expired, i.e. not in the cache */
3835 if (URLCacheContainers_FindContainerW(url, &pContainer))
3837 memset(pftLastModified, 0, sizeof(*pftLastModified));
3841 if (URLCacheContainer_OpenIndex(pContainer))
3843 memset(pftLastModified, 0, sizeof(*pftLastModified));
3847 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3849 memset(pftLastModified, 0, sizeof(*pftLastModified));
3853 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3855 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3856 memset(pftLastModified, 0, sizeof(*pftLastModified));
3857 TRACE("entry %s not found!\n", debugstr_w(url));
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 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3870 if (pEntry->dwSignature != URL_SIGNATURE)
3872 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3873 memset(pftLastModified, 0, sizeof(*pftLastModified));
3874 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3878 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3879 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3881 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3886 /***********************************************************************
3887 * GetDiskInfoA (WININET.@)
3889 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3892 ULARGE_INTEGER bytes_free, bytes_total;
3894 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3898 SetLastError(ERROR_INVALID_PARAMETER);
3902 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3904 if (cluster_size) *cluster_size = 1;
3905 if (free) *free = bytes_free.QuadPart;
3906 if (total) *total = bytes_total.QuadPart;
3911 /***********************************************************************
3912 * RegisterUrlCacheNotification (WININET.@)
3914 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3916 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3920 /***********************************************************************
3921 * IncrementUrlCacheHeaderData (WININET.@)
3923 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3925 FIXME("(%u, %p)\n", index, data);