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>
56 #include "wine/unicode.h"
57 #include "wine/debug.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
61 #define ENTRY_START_OFFSET 0x4000
64 #define HASHTABLE_SIZE 448
65 #define HASHTABLE_BLOCKSIZE 7
66 #define HASHTABLE_FREE 3
67 #define ALLOCATION_TABLE_OFFSET 0x250
68 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
69 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
70 #define NEWFILE_NUM_BLOCKS 0xd80
71 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
73 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
74 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
75 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
76 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
77 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
79 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
81 typedef struct _CACHEFILE_ENTRY
85 DWORD dwSignature; /* e.g. "URL " */
86 /* CHAR szSignature[4];
88 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
91 typedef struct _URL_CACHEFILE_ENTRY
93 CACHEFILE_ENTRY CacheFileEntry;
94 FILETIME LastModifiedTime;
95 FILETIME LastAccessTime;
96 WORD wExpiredDate; /* expire date in dos format */
97 WORD wExpiredTime; /* expire time in dos format */
98 DWORD dwUnknown1; /* usually zero */
99 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
100 DWORD dwUnknown2; /* usually zero */
101 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
102 DWORD dwUnknown3; /* usually 0x60 */
103 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
104 BYTE CacheDir; /* index of cache directory this url is stored in */
105 BYTE Unknown4; /* usually zero */
106 WORD wUnknown5; /* usually 0x1010 */
107 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
108 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
109 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
110 DWORD dwHeaderInfoSize;
111 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
112 WORD wLastSyncDate; /* last sync date in dos format */
113 WORD wLastSyncTime; /* last sync time in dos format */
114 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
115 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
116 WORD wUnknownDate; /* usually same as wLastSyncDate */
117 WORD wUnknownTime; /* usually same as wLastSyncTime */
118 DWORD dwUnknown7; /* usually zero */
119 DWORD dwUnknown8; /* usually zero */
120 /* packing to dword align start of next field */
121 /* CHAR szSourceUrlName[]; (url) */
122 /* packing to dword align start of next field */
123 /* CHAR szLocalFileName[]; (local file name excluding path) */
124 /* packing to dword align start of next field */
125 /* CHAR szHeaderInfo[]; (header info) */
126 } URL_CACHEFILE_ENTRY;
134 typedef struct _HASH_CACHEFILE_ENTRY
136 CACHEFILE_ENTRY CacheFileEntry;
138 DWORD dwHashTableNumber;
139 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
140 } HASH_CACHEFILE_ENTRY;
142 typedef struct _DIRECTORY_DATA
145 char filename[DIR_LENGTH];
148 typedef struct _URLCACHE_HEADER
150 char szSignature[28];
152 DWORD dwOffsetFirstHashTable;
153 DWORD dwIndexCapacityInBlocks;
156 ULARGE_INTEGER CacheLimit;
157 ULARGE_INTEGER CacheUsage;
158 ULARGE_INTEGER ExemptUsage;
159 DWORD DirectoryCount; /* number of directory_data's */
160 DIRECTORY_DATA directory_data[1]; /* first directory entry */
161 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
162 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
164 typedef struct _STREAM_HANDLE
170 typedef struct _URLCACHECONTAINER
172 struct list entry; /* part of a list */
173 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
174 LPWSTR path; /* path to url container directory */
175 HANDLE hMapping; /* handle of file mapping */
176 DWORD file_size; /* size of file when mapping was opened */
177 HANDLE hMutex; /* handle of mutex */
181 /* List of all containers available */
182 static struct list UrlContainers = LIST_INIT(UrlContainers);
184 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
186 /***********************************************************************
187 * URLCache_PathToObjectName (Internal)
189 * Converts a path to a name suitable for use as a Win32 object name.
190 * Replaces '\\' characters in-place with the specified character
191 * (usually '_' or '!')
197 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
199 for (; *lpszPath; lpszPath++)
201 if (*lpszPath == '\\')
206 /***********************************************************************
207 * URLCacheContainer_OpenIndex (Internal)
209 * Opens the index file and saves mapping handle in hCacheIndexMapping
212 * ERROR_SUCCESS if succeeded
213 * Any other Win32 error code if failed
216 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
219 WCHAR wszFilePath[MAX_PATH];
222 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
223 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
225 if (pContainer->hMapping)
226 return ERROR_SUCCESS;
228 strcpyW(wszFilePath, pContainer->path);
229 strcatW(wszFilePath, wszIndex);
231 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
232 if (hFile == INVALID_HANDLE_VALUE)
234 /* Maybe the directory wasn't there? Try to create it */
235 if (CreateDirectoryW(pContainer->path, 0))
236 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 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
241 return GetLastError();
244 /* At this stage we need the mutex because we may be about to create the
247 WaitForSingleObject(pContainer->hMutex, INFINITE);
249 dwFileSize = GetFileSize(hFile, NULL);
250 if (dwFileSize == INVALID_FILE_SIZE)
252 ReleaseMutex(pContainer->hMutex);
253 return GetLastError();
258 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
260 char achZeroes[0x1000];
262 DWORD dwError = ERROR_SUCCESS;
264 /* Write zeroes to the entire file so we can safely map it without
265 * fear of getting a SEGV because the disk is full.
267 memset(achZeroes, 0, sizeof(achZeroes));
268 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
270 DWORD dwWrite = sizeof(achZeroes);
273 if (NEWFILE_SIZE - dwOffset < dwWrite)
274 dwWrite = NEWFILE_SIZE - dwOffset;
275 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
276 dwWritten != dwWrite)
278 /* If we fail to write, we need to return the error that
279 * cause the problem and also make sure the file is no
280 * longer there, if possible.
282 dwError = GetLastError();
288 if (dwError == ERROR_SUCCESS)
290 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
294 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
299 WCHAR wszDirPath[MAX_PATH];
302 HASH_CACHEFILE_ENTRY *pHashEntry;
304 dwFileSize = NEWFILE_SIZE;
306 /* First set some constants and defaults in the header */
307 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
308 pHeader->dwFileSize = dwFileSize;
309 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
310 /* 127MB - taken from default for Windows 2000 */
311 pHeader->CacheLimit.QuadPart = 0x07ff5400;
312 /* Copied from a Windows 2000 cache index */
313 pHeader->DirectoryCount = 4;
315 /* If the registry has a cache size set, use the registry value */
316 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
319 DWORD len = sizeof(dw);
322 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
323 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
324 keytype == REG_DWORD)
326 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
331 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
333 /* Last step - create the directories */
335 strcpyW(wszDirPath, pContainer->path);
336 pwchDir = wszDirPath + strlenW(wszDirPath);
339 GetSystemTimeAsFileTime(&ft);
341 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
343 pHeader->directory_data[i].dwNumFiles = 0;
347 ULONGLONG n = ft.dwHighDateTime;
349 /* Generate a file name to attempt to create.
350 * This algorithm will create what will appear
351 * to be random and unrelated directory names
352 * of up to 9 characters in length.
355 n += ft.dwLowDateTime;
356 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
358 for (k = 0; k < 8; ++k)
362 /* Dividing by a prime greater than 36 helps
363 * with the appearance of randomness
368 pwchDir[k] = '0' + r;
370 pwchDir[k] = 'A' + (r - 10);
373 if (CreateDirectoryW(wszDirPath, 0))
375 /* The following is OK because we generated an
376 * 8 character directory name made from characters
377 * [A-Z0-9], which are equivalent for all code
378 * pages and for UTF-16
380 for (k = 0; k < 8; ++k)
381 pHeader->directory_data[i].filename[k] = pwchDir[k];
386 /* Give up. The most likely cause of this
387 * is a full disk, but whatever the cause
388 * is, it should be more than apparent that
391 dwError = GetLastError();
397 UnmapViewOfFile(pHeader);
401 dwError = GetLastError();
403 CloseHandle(hMapping);
407 dwError = GetLastError();
414 DeleteFileW(wszFilePath);
415 ReleaseMutex(pContainer->hMutex);
421 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 return GetLastError();
435 return ERROR_SUCCESS;
438 /***********************************************************************
439 * URLCacheContainer_CloseIndex (Internal)
447 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
449 CloseHandle(pContainer->hMapping);
450 pContainer->hMapping = NULL;
453 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
455 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
456 int cache_prefix_len = strlenW(cache_prefix);
463 pContainer->hMapping = NULL;
464 pContainer->file_size = 0;
466 pContainer->path = heap_strdupW(path);
467 if (!pContainer->path)
469 HeapFree(GetProcessHeap(), 0, pContainer);
473 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
474 if (!pContainer->cache_prefix)
476 HeapFree(GetProcessHeap(), 0, pContainer->path);
477 HeapFree(GetProcessHeap(), 0, pContainer);
481 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
483 CharLowerW(mutex_name);
484 URLCache_PathToObjectName(mutex_name, '!');
486 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
488 ERR("couldn't create mutex (error is %d)\n", GetLastError());
489 HeapFree(GetProcessHeap(), 0, pContainer->path);
490 HeapFree(GetProcessHeap(), 0, pContainer);
494 list_add_head(&UrlContainers, &pContainer->entry);
499 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
501 list_remove(&pContainer->entry);
503 URLCacheContainer_CloseIndex(pContainer);
504 CloseHandle(pContainer->hMutex);
505 HeapFree(GetProcessHeap(), 0, pContainer->path);
506 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
507 HeapFree(GetProcessHeap(), 0, pContainer);
510 void URLCacheContainers_CreateDefaults(void)
512 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
513 static const WCHAR UrlPrefix[] = {0};
514 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
515 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
516 static const WCHAR CookieSuffix[] = {0};
517 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
520 int nFolder; /* CSIDL_* constant */
521 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
522 const WCHAR * cache_prefix; /* prefix used to reference the container */
523 } DefaultContainerData[] =
525 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
526 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
527 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
531 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
533 WCHAR wszCachePath[MAX_PATH];
534 WCHAR wszMutexName[MAX_PATH];
535 int path_len, suffix_len;
537 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
539 ERR("Couldn't get path for default container %u\n", i);
542 path_len = strlenW(wszCachePath);
543 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
545 if (path_len + suffix_len + 2 > MAX_PATH)
547 ERR("Path too long\n");
551 wszCachePath[path_len] = '\\';
552 wszCachePath[path_len+1] = 0;
554 strcpyW(wszMutexName, wszCachePath);
558 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
559 wszCachePath[path_len + suffix_len + 1] = '\\';
560 wszCachePath[path_len + suffix_len + 2] = '\0';
563 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
567 void URLCacheContainers_DeleteAll(void)
569 while(!list_empty(&UrlContainers))
570 URLCacheContainer_DeleteContainer(
571 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
575 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
577 URLCACHECONTAINER * pContainer;
579 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
582 return ERROR_INVALID_PARAMETER;
584 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
586 int prefix_len = strlenW(pContainer->cache_prefix);
587 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
589 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
590 *ppContainer = pContainer;
591 return ERROR_SUCCESS;
594 ERR("no container found\n");
595 return ERROR_FILE_NOT_FOUND;
598 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
603 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
604 return ERROR_OUTOFMEMORY;
606 ret = URLCacheContainers_FindContainerW(url, ppContainer);
607 HeapFree(GetProcessHeap(), 0, url);
611 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
614 URLCACHECONTAINER * pContainer;
616 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
618 /* non-NULL search pattern only returns one container ever */
619 if (lpwszSearchPattern && dwIndex > 0)
622 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
624 if (lpwszSearchPattern)
626 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
628 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
629 *ppContainer = pContainer;
637 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
638 *ppContainer = pContainer;
647 /***********************************************************************
648 * URLCacheContainer_LockIndex (Internal)
650 * Locks the index for system-wide exclusive access.
653 * Cache file header if successful
654 * NULL if failed and calls SetLastError.
656 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
660 URLCACHE_HEADER * pHeader;
664 WaitForSingleObject(pContainer->hMutex, INFINITE);
666 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
670 ReleaseMutex(pContainer->hMutex);
671 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
674 pHeader = (URLCACHE_HEADER *)pIndexData;
676 /* file has grown - we need to remap to prevent us getting
677 * access violations when we try and access beyond the end
678 * of the memory mapped file */
679 if (pHeader->dwFileSize != pContainer->file_size)
681 UnmapViewOfFile( pHeader );
682 URLCacheContainer_CloseIndex(pContainer);
683 error = URLCacheContainer_OpenIndex(pContainer);
684 if (error != ERROR_SUCCESS)
686 ReleaseMutex(pContainer->hMutex);
690 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
694 ReleaseMutex(pContainer->hMutex);
695 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
698 pHeader = (URLCACHE_HEADER *)pIndexData;
701 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
703 for (index = 0; index < pHeader->DirectoryCount; index++)
705 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
711 /***********************************************************************
712 * URLCacheContainer_UnlockIndex (Internal)
715 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
718 ReleaseMutex(pContainer->hMutex);
719 return UnmapViewOfFile(pHeader);
724 #define CHAR_BIT (8 * sizeof(CHAR))
727 /***********************************************************************
728 * URLCache_Allocation_BlockIsFree (Internal)
730 * Is the specified block number free?
737 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
739 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
740 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
743 /***********************************************************************
744 * URLCache_Allocation_BlockFree (Internal)
746 * Marks the specified block as free
752 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
754 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
755 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
758 /***********************************************************************
759 * URLCache_Allocation_BlockAllocate (Internal)
761 * Marks the specified block as allocated
767 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
769 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
770 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
773 /***********************************************************************
774 * URLCache_FindFirstFreeEntry (Internal)
776 * Finds and allocates the first block of free space big enough and
777 * sets ppEntry to point to it.
780 * TRUE if it had enough space
781 * FALSE if it couldn't find enough space
784 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
786 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
789 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
791 for (dwFreeCounter = 0;
792 dwFreeCounter < dwBlocksNeeded &&
793 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
794 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
796 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
798 if (dwFreeCounter == dwBlocksNeeded)
801 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
802 for (index = 0; index < dwBlocksNeeded; index++)
803 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
804 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
805 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
809 FIXME("Grow file\n");
813 /***********************************************************************
814 * URLCache_DeleteEntry (Internal)
816 * Deletes the specified entry and frees the space allocated to it
819 * TRUE if it succeeded
823 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
827 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
829 /* update allocation table */
830 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
831 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
832 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
834 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
838 /***********************************************************************
839 * URLCache_LocalFileNameToPathW (Internal)
841 * Copies the full path to the specified buffer given the local file
842 * name and the index of the directory it is in. Always sets value in
843 * lpBufferSize to the required buffer size (in bytes).
846 * TRUE if the buffer was big enough
847 * FALSE if the buffer was too small
850 static BOOL URLCache_LocalFileNameToPathW(
851 const URLCACHECONTAINER * pContainer,
852 LPCURLCACHE_HEADER pHeader,
853 LPCSTR szLocalFileName,
859 int path_len = strlenW(pContainer->path);
860 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
861 if (Directory >= pHeader->DirectoryCount)
867 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
868 if (nRequired <= *lpBufferSize)
872 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
873 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
874 wszPath[dir_len + path_len] = '\\';
875 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
876 *lpBufferSize = nRequired;
879 *lpBufferSize = nRequired;
883 /***********************************************************************
884 * URLCache_LocalFileNameToPathA (Internal)
886 * Copies the full path to the specified buffer given the local file
887 * name and the index of the directory it is in. Always sets value in
888 * lpBufferSize to the required buffer size.
891 * TRUE if the buffer was big enough
892 * FALSE if the buffer was too small
895 static BOOL URLCache_LocalFileNameToPathA(
896 const URLCACHECONTAINER * pContainer,
897 LPCURLCACHE_HEADER pHeader,
898 LPCSTR szLocalFileName,
904 int path_len, file_name_len, dir_len;
906 if (Directory >= pHeader->DirectoryCount)
912 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
913 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
914 dir_len = DIR_LENGTH;
916 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
917 if (nRequired < *lpBufferSize)
919 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
920 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
921 szPath[path_len + dir_len] = '\\';
922 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
923 *lpBufferSize = nRequired;
926 *lpBufferSize = nRequired;
930 /* Just like DosDateTimeToFileTime, except that it also maps the special
931 * case of a DOS date/time of (0,0) to a filetime of (0,0).
933 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
936 if (!fatdate && !fattime)
937 ft->dwLowDateTime = ft->dwHighDateTime = 0;
939 DosDateTimeToFileTime(fatdate, fattime, ft);
942 /***********************************************************************
943 * URLCache_CopyEntry (Internal)
945 * Copies an entry from the cache index file to the Win32 structure
948 * ERROR_SUCCESS if the buffer was big enough
949 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
952 static DWORD URLCache_CopyEntry(
953 URLCACHECONTAINER * pContainer,
954 LPCURLCACHE_HEADER pHeader,
955 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
956 LPDWORD lpdwBufferSize,
957 const URL_CACHEFILE_ENTRY * pUrlEntry,
961 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
963 if (*lpdwBufferSize >= dwRequiredSize)
965 lpCacheEntryInfo->lpHeaderInfo = NULL;
966 lpCacheEntryInfo->lpszFileExtension = NULL;
967 lpCacheEntryInfo->lpszLocalFileName = NULL;
968 lpCacheEntryInfo->lpszSourceUrlName = NULL;
969 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
970 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
971 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
972 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
973 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
974 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
975 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
976 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
977 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
978 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
979 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
980 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
981 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
982 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
985 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
986 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
987 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
989 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
991 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
992 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
994 /* FIXME: is source url optional? */
995 if (*lpdwBufferSize >= dwRequiredSize)
997 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
999 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1001 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1003 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1006 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1007 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1008 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1010 if (pUrlEntry->dwOffsetLocalName)
1012 LONG nLocalFilePathSize;
1013 LPSTR lpszLocalFileName;
1014 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1015 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1016 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1017 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1019 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1021 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1023 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1024 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1025 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1027 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1029 if (*lpdwBufferSize >= dwRequiredSize)
1031 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1032 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1033 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1035 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1036 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1037 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1039 if (pUrlEntry->dwOffsetFileExtension)
1044 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1046 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1047 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1049 if (*lpdwBufferSize >= dwRequiredSize)
1051 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1053 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1055 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1058 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1059 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1060 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1063 if (dwRequiredSize > *lpdwBufferSize)
1065 *lpdwBufferSize = dwRequiredSize;
1066 return ERROR_INSUFFICIENT_BUFFER;
1068 *lpdwBufferSize = dwRequiredSize;
1069 return ERROR_SUCCESS;
1072 /* Just like FileTimeToDosDateTime, except that it also maps the special
1073 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1075 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1078 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1079 *fatdate = *fattime = 0;
1081 FileTimeToDosDateTime(ft, fatdate, fattime);
1084 /***********************************************************************
1085 * URLCache_SetEntryInfo (Internal)
1087 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1088 * according to the flags set by dwFieldControl.
1091 * ERROR_SUCCESS if the buffer was big enough
1092 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1095 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1097 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1098 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1099 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1100 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1101 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1102 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1103 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1104 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1105 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1106 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1107 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1108 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1109 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1110 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1111 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1112 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1114 return ERROR_SUCCESS;
1117 /***********************************************************************
1118 * URLCache_HashKey (Internal)
1120 * Returns the hash key for a given string
1123 * hash key for the string
1126 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1128 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1129 * but the algorithm and result are not the same!
1131 static const unsigned char lookupTable[256] =
1133 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1134 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1135 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1136 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1137 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1138 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1139 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1140 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1141 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1142 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1143 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1144 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1145 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1146 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1147 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1148 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1149 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1150 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1151 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1152 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1153 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1154 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1155 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1156 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1157 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1158 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1159 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1160 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1161 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1162 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1163 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1164 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1169 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1170 key[i] = lookupTable[i];
1172 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1174 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1175 key[i] = lookupTable[*lpszKey ^ key[i]];
1178 return *(DWORD *)key;
1181 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1183 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1186 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1188 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1189 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1190 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1193 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1195 /* structure of hash table:
1196 * 448 entries divided into 64 blocks
1197 * each block therefore contains a chain of 7 key/offset pairs
1198 * how position in table is calculated:
1199 * 1. the url is hashed in helper function
1200 * 2. the key % 64 * 8 is the offset
1201 * 3. the key in the hash table is the hash key aligned to 64
1204 * there can be multiple hash tables in the file and the offset to
1205 * the next one is stored in the header of the hash table
1207 DWORD key = URLCache_HashKey(lpszUrl);
1208 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1209 HASH_CACHEFILE_ENTRY * pHashEntry;
1210 DWORD dwHashTableNumber = 0;
1212 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1214 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1215 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1216 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1219 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1221 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1224 /* make sure that it is in fact a hash entry */
1225 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1227 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1231 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1233 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1234 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1236 /* FIXME: we should make sure that this is the right element
1237 * before returning and claiming that it is. We can do this
1238 * by doing a simple compare between the URL we were given
1239 * and the URL stored in the entry. However, this assumes
1240 * we know the format of all the entries stored in the
1242 *ppHashEntry = pHashElement;
1250 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1255 urlA = heap_strdupWtoA(lpszUrl);
1258 SetLastError(ERROR_OUTOFMEMORY);
1262 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1263 HeapFree(GetProcessHeap(), 0, urlA);
1267 /***********************************************************************
1268 * URLCache_HashEntrySetUse (Internal)
1270 * Searches all the hash tables in the index for the given URL and
1271 * sets the use count (stored or'ed with key)
1274 * TRUE if the entry was found
1275 * FALSE if the entry could not be found
1278 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1280 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1284 /***********************************************************************
1285 * URLCache_DeleteEntryFromHash (Internal)
1287 * Searches all the hash tables in the index for the given URL and
1288 * then if found deletes the entry.
1291 * TRUE if the entry was found
1292 * FALSE if the entry could not be found
1295 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1297 pHashEntry->dwHashKey = HASHTABLE_FREE;
1298 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1302 /***********************************************************************
1303 * URLCache_AddEntryToHash (Internal)
1305 * Searches all the hash tables for a free slot based on the offset
1306 * generated from the hash key. If a free slot is found, the offset and
1307 * key are entered into the hash table.
1310 * ERROR_SUCCESS if the entry was added
1311 * Any other Win32 error code if the entry could not be added
1314 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1316 /* see URLCache_FindEntryInHash for structure of hash tables */
1318 DWORD key = URLCache_HashKey(lpszUrl);
1319 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1320 HASH_CACHEFILE_ENTRY * pHashEntry;
1321 DWORD dwHashTableNumber = 0;
1324 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1326 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1327 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1328 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1331 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1333 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1336 /* make sure that it is in fact a hash entry */
1337 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1339 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1343 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1345 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1346 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1348 pHashElement->dwHashKey = key;
1349 pHashElement->dwOffsetEntry = dwOffsetEntry;
1350 return ERROR_SUCCESS;
1354 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1355 if (error != ERROR_SUCCESS)
1358 pHashEntry->HashTable[offset].dwHashKey = key;
1359 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1360 return ERROR_SUCCESS;
1363 /***********************************************************************
1364 * URLCache_CreateHashTable (Internal)
1366 * Creates a new hash table in free space and adds it to the chain of existing
1370 * ERROR_SUCCESS if the hash table was created
1371 * ERROR_DISK_FULL if the hash table could not be created
1374 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1379 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1381 FIXME("no free space for hash table\n");
1382 return ERROR_DISK_FULL;
1385 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1388 pPrevHash->dwAddressNext = dwOffset;
1390 pHeader->dwOffsetFirstHashTable = dwOffset;
1391 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1392 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1393 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1394 for (i = 0; i < HASHTABLE_SIZE; i++)
1396 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1397 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1399 return ERROR_SUCCESS;
1402 /***********************************************************************
1403 * URLCache_EnumHashTables (Internal)
1405 * Enumerates the hash tables in a container.
1408 * TRUE if an entry was found
1409 * FALSE if there are no more tables to enumerate.
1412 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1414 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1415 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1416 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1418 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1419 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1421 /* make sure that it is in fact a hash entry */
1422 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1424 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1425 (*pdwHashTableNumber)++;
1429 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1435 /***********************************************************************
1436 * URLCache_EnumHashTableEntries (Internal)
1438 * Enumerates entries in a hash table and returns the next non-free entry.
1441 * TRUE if an entry was found
1442 * FALSE if the hash table is empty or there are no more entries to
1446 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1447 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1449 for (; *index < HASHTABLE_SIZE ; (*index)++)
1451 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1454 *ppHashEntry = &pHashEntry->HashTable[*index];
1455 TRACE("entry found %d\n", *index);
1458 TRACE("no more entries (%d)\n", *index);
1462 /***********************************************************************
1463 * FreeUrlCacheSpaceA (WININET.@)
1466 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
1469 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1473 /***********************************************************************
1474 * FreeUrlCacheSpaceW (WININET.@)
1477 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
1480 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1484 /***********************************************************************
1485 * GetUrlCacheEntryInfoExA (WININET.@)
1488 BOOL WINAPI GetUrlCacheEntryInfoExA(
1490 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1491 LPDWORD lpdwCacheEntryInfoBufSize,
1493 LPDWORD lpdwReserved,
1497 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1498 debugstr_a(lpszUrl),
1500 lpdwCacheEntryInfoBufSize,
1506 if ((lpszReserved != NULL) ||
1507 (lpdwReserved != NULL) ||
1508 (lpReserved != NULL))
1510 ERR("Reserved value was not 0\n");
1511 SetLastError(ERROR_INVALID_PARAMETER);
1516 FIXME("Undocumented flag(s): %x\n", dwFlags);
1517 SetLastError(ERROR_FILE_NOT_FOUND);
1520 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1523 /***********************************************************************
1524 * GetUrlCacheEntryInfoA (WININET.@)
1527 BOOL WINAPI GetUrlCacheEntryInfoA(
1528 IN LPCSTR lpszUrlName,
1529 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1530 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1533 LPURLCACHE_HEADER pHeader;
1534 struct _HASH_ENTRY * pHashEntry;
1535 const CACHEFILE_ENTRY * pEntry;
1536 const URL_CACHEFILE_ENTRY * pUrlEntry;
1537 URLCACHECONTAINER * pContainer;
1540 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1542 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1543 if (error != ERROR_SUCCESS)
1545 SetLastError(error);
1549 error = URLCacheContainer_OpenIndex(pContainer);
1550 if (error != ERROR_SUCCESS)
1552 SetLastError(error);
1556 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1559 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1561 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1562 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1563 SetLastError(ERROR_FILE_NOT_FOUND);
1567 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1568 if (pEntry->dwSignature != URL_SIGNATURE)
1570 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1571 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1572 SetLastError(ERROR_FILE_NOT_FOUND);
1576 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1577 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1578 if (pUrlEntry->dwOffsetHeaderInfo)
1579 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1581 if (lpdwCacheEntryInfoBufferSize)
1583 if (!lpCacheEntryInfo)
1584 *lpdwCacheEntryInfoBufferSize = 0;
1586 error = URLCache_CopyEntry(
1590 lpdwCacheEntryInfoBufferSize,
1593 if (error != ERROR_SUCCESS)
1595 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1596 SetLastError(error);
1599 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1602 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1607 /***********************************************************************
1608 * GetUrlCacheEntryInfoW (WININET.@)
1611 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1612 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1613 LPDWORD lpdwCacheEntryInfoBufferSize)
1615 LPURLCACHE_HEADER pHeader;
1616 struct _HASH_ENTRY * pHashEntry;
1617 const CACHEFILE_ENTRY * pEntry;
1618 const URL_CACHEFILE_ENTRY * pUrlEntry;
1619 URLCACHECONTAINER * pContainer;
1622 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1624 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1625 if (error != ERROR_SUCCESS)
1627 SetLastError(error);
1631 error = URLCacheContainer_OpenIndex(pContainer);
1632 if (error != ERROR_SUCCESS)
1634 SetLastError(error);
1638 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1641 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1643 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1644 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1645 SetLastError(ERROR_FILE_NOT_FOUND);
1649 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1650 if (pEntry->dwSignature != URL_SIGNATURE)
1652 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1653 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1654 SetLastError(ERROR_FILE_NOT_FOUND);
1658 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1659 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1660 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1662 if (lpdwCacheEntryInfoBufferSize)
1664 if (!lpCacheEntryInfo)
1665 *lpdwCacheEntryInfoBufferSize = 0;
1667 error = URLCache_CopyEntry(
1670 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1671 lpdwCacheEntryInfoBufferSize,
1673 TRUE /* UNICODE */);
1674 if (error != ERROR_SUCCESS)
1676 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1677 SetLastError(error);
1680 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1683 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1688 /***********************************************************************
1689 * GetUrlCacheEntryInfoExW (WININET.@)
1692 BOOL WINAPI GetUrlCacheEntryInfoExW(
1694 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1695 LPDWORD lpdwCacheEntryInfoBufSize,
1696 LPWSTR lpszReserved,
1697 LPDWORD lpdwReserved,
1701 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1702 debugstr_w(lpszUrl),
1704 lpdwCacheEntryInfoBufSize,
1710 if ((lpszReserved != NULL) ||
1711 (lpdwReserved != NULL) ||
1712 (lpReserved != NULL))
1714 ERR("Reserved value was not 0\n");
1715 SetLastError(ERROR_INVALID_PARAMETER);
1720 FIXME("Undocumented flag(s): %x\n", dwFlags);
1721 SetLastError(ERROR_FILE_NOT_FOUND);
1724 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1727 /***********************************************************************
1728 * SetUrlCacheEntryInfoA (WININET.@)
1730 BOOL WINAPI SetUrlCacheEntryInfoA(
1732 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1733 DWORD dwFieldControl)
1735 LPURLCACHE_HEADER pHeader;
1736 struct _HASH_ENTRY * pHashEntry;
1737 CACHEFILE_ENTRY * pEntry;
1738 URLCACHECONTAINER * pContainer;
1741 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1743 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1744 if (error != ERROR_SUCCESS)
1746 SetLastError(error);
1750 error = URLCacheContainer_OpenIndex(pContainer);
1751 if (error != ERROR_SUCCESS)
1753 SetLastError(error);
1757 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1760 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1762 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1763 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1764 SetLastError(ERROR_FILE_NOT_FOUND);
1768 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1769 if (pEntry->dwSignature != URL_SIGNATURE)
1771 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1772 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1773 SetLastError(ERROR_FILE_NOT_FOUND);
1777 URLCache_SetEntryInfo(
1778 (URL_CACHEFILE_ENTRY *)pEntry,
1779 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1782 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1787 /***********************************************************************
1788 * SetUrlCacheEntryInfoW (WININET.@)
1790 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1792 LPURLCACHE_HEADER pHeader;
1793 struct _HASH_ENTRY * pHashEntry;
1794 CACHEFILE_ENTRY * pEntry;
1795 URLCACHECONTAINER * pContainer;
1798 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1800 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1801 if (error != ERROR_SUCCESS)
1803 SetLastError(error);
1807 error = URLCacheContainer_OpenIndex(pContainer);
1808 if (error != ERROR_SUCCESS)
1810 SetLastError(error);
1814 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1817 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1819 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1820 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1821 SetLastError(ERROR_FILE_NOT_FOUND);
1825 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1826 if (pEntry->dwSignature != URL_SIGNATURE)
1828 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1829 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1830 SetLastError(ERROR_FILE_NOT_FOUND);
1834 URLCache_SetEntryInfo(
1835 (URL_CACHEFILE_ENTRY *)pEntry,
1839 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1844 /***********************************************************************
1845 * RetrieveUrlCacheEntryFileA (WININET.@)
1848 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1849 IN LPCSTR lpszUrlName,
1850 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1851 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1855 LPURLCACHE_HEADER pHeader;
1856 struct _HASH_ENTRY * pHashEntry;
1857 CACHEFILE_ENTRY * pEntry;
1858 URL_CACHEFILE_ENTRY * pUrlEntry;
1859 URLCACHECONTAINER * pContainer;
1862 TRACE("(%s, %p, %p, 0x%08x)\n",
1863 debugstr_a(lpszUrlName),
1865 lpdwCacheEntryInfoBufferSize,
1868 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1869 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1871 SetLastError(ERROR_INVALID_PARAMETER);
1875 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1876 if (error != ERROR_SUCCESS)
1878 SetLastError(error);
1882 error = URLCacheContainer_OpenIndex(pContainer);
1883 if (error != ERROR_SUCCESS)
1885 SetLastError(error);
1889 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1892 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1894 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1895 TRACE("entry %s not found!\n", lpszUrlName);
1896 SetLastError(ERROR_FILE_NOT_FOUND);
1900 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1901 if (pEntry->dwSignature != URL_SIGNATURE)
1903 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1904 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1905 SetLastError(ERROR_FILE_NOT_FOUND);
1909 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1910 if (!pUrlEntry->dwOffsetLocalName)
1912 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1913 SetLastError(ERROR_INVALID_DATA);
1917 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1918 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1920 pUrlEntry->dwHitRate++;
1921 pUrlEntry->dwUseCount++;
1922 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1923 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1925 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
1926 lpdwCacheEntryInfoBufferSize, pUrlEntry,
1928 if (error != ERROR_SUCCESS)
1930 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931 SetLastError(error);
1934 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1936 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1941 /***********************************************************************
1942 * RetrieveUrlCacheEntryFileW (WININET.@)
1945 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1946 IN LPCWSTR lpszUrlName,
1947 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1948 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1952 LPURLCACHE_HEADER pHeader;
1953 struct _HASH_ENTRY * pHashEntry;
1954 CACHEFILE_ENTRY * pEntry;
1955 URL_CACHEFILE_ENTRY * pUrlEntry;
1956 URLCACHECONTAINER * pContainer;
1959 TRACE("(%s, %p, %p, 0x%08x)\n",
1960 debugstr_w(lpszUrlName),
1962 lpdwCacheEntryInfoBufferSize,
1965 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1966 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1968 SetLastError(ERROR_INVALID_PARAMETER);
1972 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
1973 if (error != ERROR_SUCCESS)
1975 SetLastError(error);
1979 error = URLCacheContainer_OpenIndex(pContainer);
1980 if (error != ERROR_SUCCESS)
1982 SetLastError(error);
1986 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1989 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1991 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1992 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1993 SetLastError(ERROR_FILE_NOT_FOUND);
1997 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1998 if (pEntry->dwSignature != URL_SIGNATURE)
2000 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2001 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2002 SetLastError(ERROR_FILE_NOT_FOUND);
2006 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2007 if (!pUrlEntry->dwOffsetLocalName)
2009 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2010 SetLastError(ERROR_INVALID_DATA);
2014 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2015 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2017 pUrlEntry->dwHitRate++;
2018 pUrlEntry->dwUseCount++;
2019 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2020 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2022 error = URLCache_CopyEntry(
2025 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2026 lpdwCacheEntryInfoBufferSize,
2028 TRUE /* UNICODE */);
2029 if (error != ERROR_SUCCESS)
2031 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2032 SetLastError(error);
2035 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2037 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2042 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2043 struct _HASH_ENTRY *pHashEntry)
2045 CACHEFILE_ENTRY * pEntry;
2046 URL_CACHEFILE_ENTRY * pUrlEntry;
2048 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2049 if (pEntry->dwSignature != URL_SIGNATURE)
2051 FIXME("Trying to delete entry of unknown format %s\n",
2052 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2053 SetLastError(ERROR_FILE_NOT_FOUND);
2056 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2057 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2059 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2060 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2062 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2064 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2065 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2067 pHeader->ExemptUsage.QuadPart = 0;
2071 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2072 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2074 pHeader->CacheUsage.QuadPart = 0;
2077 URLCache_DeleteEntry(pHeader, pEntry);
2079 URLCache_DeleteEntryFromHash(pHashEntry);
2083 /***********************************************************************
2084 * UnlockUrlCacheEntryFileA (WININET.@)
2087 BOOL WINAPI UnlockUrlCacheEntryFileA(
2088 IN LPCSTR lpszUrlName,
2092 LPURLCACHE_HEADER pHeader;
2093 struct _HASH_ENTRY * pHashEntry;
2094 CACHEFILE_ENTRY * pEntry;
2095 URL_CACHEFILE_ENTRY * pUrlEntry;
2096 URLCACHECONTAINER * pContainer;
2099 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2103 ERR("dwReserved != 0\n");
2104 SetLastError(ERROR_INVALID_PARAMETER);
2108 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2109 if (error != ERROR_SUCCESS)
2111 SetLastError(error);
2115 error = URLCacheContainer_OpenIndex(pContainer);
2116 if (error != ERROR_SUCCESS)
2118 SetLastError(error);
2122 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2125 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2127 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2128 TRACE("entry %s not found!\n", lpszUrlName);
2129 SetLastError(ERROR_FILE_NOT_FOUND);
2133 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2134 if (pEntry->dwSignature != URL_SIGNATURE)
2136 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2137 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2138 SetLastError(ERROR_FILE_NOT_FOUND);
2142 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2144 if (pUrlEntry->dwUseCount == 0)
2146 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2149 pUrlEntry->dwUseCount--;
2150 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2152 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2157 /***********************************************************************
2158 * UnlockUrlCacheEntryFileW (WININET.@)
2161 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2163 LPURLCACHE_HEADER pHeader;
2164 struct _HASH_ENTRY * pHashEntry;
2165 CACHEFILE_ENTRY * pEntry;
2166 URL_CACHEFILE_ENTRY * pUrlEntry;
2167 URLCACHECONTAINER * pContainer;
2170 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2174 ERR("dwReserved != 0\n");
2175 SetLastError(ERROR_INVALID_PARAMETER);
2179 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2180 if (error != ERROR_SUCCESS)
2182 SetLastError(error);
2186 error = URLCacheContainer_OpenIndex(pContainer);
2187 if (error != ERROR_SUCCESS)
2189 SetLastError(error);
2193 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2196 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2198 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2199 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2200 SetLastError(ERROR_FILE_NOT_FOUND);
2204 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2205 if (pEntry->dwSignature != URL_SIGNATURE)
2207 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2208 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2209 SetLastError(ERROR_FILE_NOT_FOUND);
2213 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2215 if (pUrlEntry->dwUseCount == 0)
2217 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2220 pUrlEntry->dwUseCount--;
2221 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2223 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2228 /***********************************************************************
2229 * CreateUrlCacheEntryA (WININET.@)
2232 BOOL WINAPI CreateUrlCacheEntryA(
2233 IN LPCSTR lpszUrlName,
2234 IN DWORD dwExpectedFileSize,
2235 IN LPCSTR lpszFileExtension,
2236 OUT LPSTR lpszFileName,
2241 WCHAR *file_extension = NULL;
2242 WCHAR file_name[MAX_PATH];
2243 BOOL bSuccess = FALSE;
2246 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2247 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2249 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2251 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2253 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2255 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2261 dwError = GetLastError();
2266 dwError = GetLastError();
2268 HeapFree(GetProcessHeap(), 0, file_extension);
2272 dwError = GetLastError();
2274 HeapFree(GetProcessHeap(), 0, url_name);
2276 SetLastError(dwError);
2280 /***********************************************************************
2281 * CreateUrlCacheEntryW (WININET.@)
2284 BOOL WINAPI CreateUrlCacheEntryW(
2285 IN LPCWSTR lpszUrlName,
2286 IN DWORD dwExpectedFileSize,
2287 IN LPCWSTR lpszFileExtension,
2288 OUT LPWSTR lpszFileName,
2292 URLCACHECONTAINER * pContainer;
2293 LPURLCACHE_HEADER pHeader;
2294 CHAR szFile[MAX_PATH];
2295 WCHAR szExtension[MAX_PATH];
2296 LPCWSTR lpszUrlPart;
2298 LPCWSTR lpszFileNameExtension;
2299 LPWSTR lpszFileNameNoPath;
2301 int countnoextension;
2304 BOOL bFound = FALSE;
2310 static const WCHAR szWWW[] = {'w','w','w',0};
2311 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2313 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2314 debugstr_w(lpszUrlName),
2316 debugstr_w(lpszFileExtension),
2321 FIXME("dwReserved 0x%08x\n", dwReserved);
2323 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2325 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2328 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2330 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2332 lpszUrlEnd = lpszUrlPart;
2334 for (lpszUrlPart = lpszUrlEnd;
2335 (lpszUrlPart >= lpszUrlName);
2338 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2345 if (!lstrcmpW(lpszUrlPart, szWWW))
2347 lpszUrlPart += lstrlenW(szWWW);
2350 count = lpszUrlEnd - lpszUrlPart;
2352 if (bFound && (count < MAX_PATH))
2354 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2358 while(len && szFile[--len] == '/') szFile[len] = '\0';
2360 /* FIXME: get rid of illegal characters like \, / and : */
2364 FIXME("need to generate a random filename\n");
2367 TRACE("File name: %s\n", debugstr_a(szFile));
2369 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2370 if (error != ERROR_SUCCESS)
2372 SetLastError(error);
2376 error = URLCacheContainer_OpenIndex(pContainer);
2377 if (error != ERROR_SUCCESS)
2379 SetLastError(error);
2383 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2386 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2388 lBufferSize = MAX_PATH * sizeof(WCHAR);
2389 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2391 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2392 debugstr_a(szFile), lBufferSize);
2393 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2397 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2399 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2400 lpszFileNameNoPath >= lpszFileName;
2401 --lpszFileNameNoPath)
2403 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2407 countnoextension = lstrlenW(lpszFileNameNoPath);
2408 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2409 if (lpszFileNameExtension)
2410 countnoextension -= lstrlenW(lpszFileNameExtension);
2411 *szExtension = '\0';
2413 if (lpszFileExtension)
2415 szExtension[0] = '.';
2416 lstrcpyW(szExtension+1, lpszFileExtension);
2419 for (i = 0; i < 255; i++)
2421 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2424 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2425 for (p = lpszFileNameNoPath + 1; *p; p++)
2431 case '/': case '\\':
2438 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2440 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2441 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2442 if (hFile != INVALID_HANDLE_VALUE)
2449 GetSystemTimeAsFileTime(&ft);
2450 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2452 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2453 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2454 if (hFile != INVALID_HANDLE_VALUE)
2460 WARN("Could not find a unique filename\n");
2465 /***********************************************************************
2466 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2468 * The bug we are compensating for is that some drongo at Microsoft
2469 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2470 * As a consequence, CommitUrlCacheEntryA has been effectively
2471 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2472 * is still defined as LPCWSTR. The result (other than madness) is
2473 * that we always need to store lpHeaderInfo in CP_ACP rather than
2474 * in UTF16, and we need to avoid converting lpHeaderInfo in
2475 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2476 * result will lose data for arbitrary binary data.
2479 static BOOL CommitUrlCacheEntryInternal(
2480 IN LPCWSTR lpszUrlName,
2481 IN LPCWSTR lpszLocalFileName,
2482 IN FILETIME ExpireTime,
2483 IN FILETIME LastModifiedTime,
2484 IN DWORD CacheEntryType,
2485 IN LPBYTE lpHeaderInfo,
2486 IN DWORD dwHeaderSize,
2487 IN LPCWSTR lpszFileExtension,
2488 IN LPCWSTR lpszOriginalUrl
2491 URLCACHECONTAINER * pContainer;
2492 LPURLCACHE_HEADER pHeader;
2493 struct _HASH_ENTRY * pHashEntry;
2494 CACHEFILE_ENTRY * pEntry;
2495 URL_CACHEFILE_ENTRY * pUrlEntry;
2496 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2497 DWORD dwOffsetLocalFileName = 0;
2498 DWORD dwOffsetHeader = 0;
2499 DWORD dwOffsetFileExtension = 0;
2500 LARGE_INTEGER file_size;
2501 BYTE cDirectory = 0;
2502 char achFile[MAX_PATH];
2503 LPSTR lpszUrlNameA = NULL;
2504 LPSTR lpszFileExtensionA = NULL;
2505 char *pchLocalFileName = 0;
2508 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2509 debugstr_w(lpszUrlName),
2510 debugstr_w(lpszLocalFileName),
2514 debugstr_w(lpszFileExtension),
2515 debugstr_w(lpszOriginalUrl));
2517 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2519 SetLastError(ERROR_INVALID_PARAMETER);
2522 if (lpszOriginalUrl)
2523 WARN(": lpszOriginalUrl ignored\n");
2525 file_size.QuadPart = 0;
2526 if (lpszLocalFileName)
2530 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2531 if (hFile == INVALID_HANDLE_VALUE)
2533 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2538 if (!GetFileSizeEx(hFile, &file_size))
2540 ERR("couldn't get file size (error is %d)\n", GetLastError());
2548 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2549 if (error != ERROR_SUCCESS)
2551 SetLastError(error);
2555 error = URLCacheContainer_OpenIndex(pContainer);
2556 if (error != ERROR_SUCCESS)
2558 SetLastError(error);
2562 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2565 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2568 error = GetLastError();
2572 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2574 error = GetLastError();
2578 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2580 FIXME("entry already in cache - don't know what to do!\n");
2582 * SetLastError(ERROR_FILE_NOT_FOUND);
2588 if (lpszLocalFileName)
2590 BOOL bFound = FALSE;
2592 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2594 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2595 error = ERROR_INVALID_PARAMETER;
2599 /* skip container path prefix */
2600 lpszLocalFileName += lstrlenW(pContainer->path);
2602 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2603 pchLocalFileName = achFile;
2605 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2607 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2616 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2617 error = ERROR_INVALID_PARAMETER;
2621 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2624 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2625 if (lpszLocalFileName)
2627 dwOffsetLocalFileName = dwBytesNeeded;
2628 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2632 dwOffsetHeader = dwBytesNeeded;
2633 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2635 if (lpszFileExtensionA)
2637 dwOffsetFileExtension = dwBytesNeeded;
2638 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2641 /* round up to next block */
2642 if (dwBytesNeeded % BLOCKSIZE)
2644 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2645 dwBytesNeeded += BLOCKSIZE;
2648 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2650 ERR("no free entries\n");
2651 error = ERROR_DISK_FULL;
2655 /* FindFirstFreeEntry fills in blocks used */
2656 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2657 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2658 pUrlEntry->CacheDir = cDirectory;
2659 pUrlEntry->CacheEntryType = CacheEntryType;
2660 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2661 if (CacheEntryType & STICKY_CACHE_ENTRY)
2663 /* Sticky entries have a default exempt time of one day */
2664 pUrlEntry->dwExemptDelta = 86400;
2667 pUrlEntry->dwExemptDelta = 0;
2668 pUrlEntry->dwHitRate = 0;
2669 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2670 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2671 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2672 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2673 pUrlEntry->size.QuadPart = file_size.QuadPart;
2674 pUrlEntry->dwUseCount = 0;
2675 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2676 pUrlEntry->LastModifiedTime = LastModifiedTime;
2677 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2678 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2679 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2680 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2683 pUrlEntry->dwUnknown1 = 0;
2684 pUrlEntry->dwUnknown2 = 0;
2685 pUrlEntry->dwUnknown3 = 0x60;
2686 pUrlEntry->Unknown4 = 0;
2687 pUrlEntry->wUnknown5 = 0x1010;
2688 pUrlEntry->dwUnknown7 = 0;
2689 pUrlEntry->dwUnknown8 = 0;
2692 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2693 if (dwOffsetLocalFileName)
2694 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2696 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2697 if (dwOffsetFileExtension)
2698 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2700 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2701 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2702 if (error != ERROR_SUCCESS)
2703 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2706 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2707 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2708 if (CacheEntryType & STICKY_CACHE_ENTRY)
2709 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2711 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2712 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2713 pHeader->CacheLimit.QuadPart)
2714 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2718 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2719 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2720 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2722 if (error == ERROR_SUCCESS)
2726 SetLastError(error);
2731 /***********************************************************************
2732 * CommitUrlCacheEntryA (WININET.@)
2735 BOOL WINAPI CommitUrlCacheEntryA(
2736 IN LPCSTR lpszUrlName,
2737 IN LPCSTR lpszLocalFileName,
2738 IN FILETIME ExpireTime,
2739 IN FILETIME LastModifiedTime,
2740 IN DWORD CacheEntryType,
2741 IN LPBYTE lpHeaderInfo,
2742 IN DWORD dwHeaderSize,
2743 IN LPCSTR lpszFileExtension,
2744 IN LPCSTR lpszOriginalUrl
2747 WCHAR *url_name = NULL;
2748 WCHAR *local_file_name = NULL;
2749 WCHAR *original_url = NULL;
2750 WCHAR *file_extension = NULL;
2751 BOOL bSuccess = FALSE;
2753 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2754 debugstr_a(lpszUrlName),
2755 debugstr_a(lpszLocalFileName),
2759 debugstr_a(lpszFileExtension),
2760 debugstr_a(lpszOriginalUrl));
2762 url_name = heap_strdupAtoW(lpszUrlName);
2766 if (lpszLocalFileName)
2768 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2769 if (!local_file_name)
2772 if (lpszFileExtension)
2774 file_extension = heap_strdupAtoW(lpszFileExtension);
2775 if (!file_extension)
2778 if (lpszOriginalUrl)
2780 original_url = heap_strdupAtoW(lpszOriginalUrl);
2785 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2786 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2787 file_extension, original_url);
2790 HeapFree(GetProcessHeap(), 0, original_url);
2791 HeapFree(GetProcessHeap(), 0, file_extension);
2792 HeapFree(GetProcessHeap(), 0, local_file_name);
2793 HeapFree(GetProcessHeap(), 0, url_name);
2798 /***********************************************************************
2799 * CommitUrlCacheEntryW (WININET.@)
2802 BOOL WINAPI CommitUrlCacheEntryW(
2803 IN LPCWSTR lpszUrlName,
2804 IN LPCWSTR lpszLocalFileName,
2805 IN FILETIME ExpireTime,
2806 IN FILETIME LastModifiedTime,
2807 IN DWORD CacheEntryType,
2808 IN LPWSTR lpHeaderInfo,
2809 IN DWORD dwHeaderSize,
2810 IN LPCWSTR lpszFileExtension,
2811 IN LPCWSTR lpszOriginalUrl
2815 BOOL bSuccess = FALSE;
2817 CHAR *header_info = NULL;
2819 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2820 debugstr_w(lpszUrlName),
2821 debugstr_w(lpszLocalFileName),
2825 debugstr_w(lpszFileExtension),
2826 debugstr_w(lpszOriginalUrl));
2828 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2830 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2831 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2837 dwError = GetLastError();
2841 HeapFree(GetProcessHeap(), 0, header_info);
2843 SetLastError(dwError);
2849 /***********************************************************************
2850 * ReadUrlCacheEntryStream (WININET.@)
2853 BOOL WINAPI ReadUrlCacheEntryStream(
2854 IN HANDLE hUrlCacheStream,
2855 IN DWORD dwLocation,
2856 IN OUT LPVOID lpBuffer,
2857 IN OUT LPDWORD lpdwLen,
2861 /* Get handle to file from 'stream' */
2862 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2864 if (dwReserved != 0)
2866 ERR("dwReserved != 0\n");
2867 SetLastError(ERROR_INVALID_PARAMETER);
2871 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2873 SetLastError(ERROR_INVALID_HANDLE);
2877 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2879 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2882 /***********************************************************************
2883 * RetrieveUrlCacheEntryStreamA (WININET.@)
2886 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2887 IN LPCSTR lpszUrlName,
2888 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2889 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2890 IN BOOL fRandomRead,
2894 /* NOTE: this is not the same as the way that the native
2895 * version allocates 'stream' handles. I did it this way
2896 * as it is much easier and no applications should depend
2897 * on this behaviour. (Native version appears to allocate
2898 * indices into a table)
2900 STREAM_HANDLE * pStream;
2903 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2904 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2906 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2908 lpdwCacheEntryInfoBufferSize,
2914 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2919 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2921 if (hFile == INVALID_HANDLE_VALUE)
2924 /* allocate handle storage space */
2925 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2929 SetLastError(ERROR_OUTOFMEMORY);
2933 pStream->hFile = hFile;
2934 strcpy(pStream->lpszUrl, lpszUrlName);
2938 /***********************************************************************
2939 * RetrieveUrlCacheEntryStreamW (WININET.@)
2942 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2943 IN LPCWSTR lpszUrlName,
2944 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2945 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2946 IN BOOL fRandomRead,
2952 /* NOTE: this is not the same as the way that the native
2953 * version allocates 'stream' handles. I did it this way
2954 * as it is much easier and no applications should depend
2955 * on this behaviour. (Native version appears to allocate
2956 * indices into a table)
2958 STREAM_HANDLE * pStream;
2961 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2962 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2964 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
2966 lpdwCacheEntryInfoBufferSize,
2972 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
2977 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2979 if (hFile == INVALID_HANDLE_VALUE)
2982 /* allocate handle storage space */
2983 size = sizeof(STREAM_HANDLE);
2984 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2986 pStream = HeapAlloc(GetProcessHeap(), 0, size);
2990 SetLastError(ERROR_OUTOFMEMORY);
2994 pStream->hFile = hFile;
2995 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
2999 /***********************************************************************
3000 * UnlockUrlCacheEntryStream (WININET.@)
3003 BOOL WINAPI UnlockUrlCacheEntryStream(
3004 IN HANDLE hUrlCacheStream,
3008 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3010 if (dwReserved != 0)
3012 ERR("dwReserved != 0\n");
3013 SetLastError(ERROR_INVALID_PARAMETER);
3017 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3019 SetLastError(ERROR_INVALID_HANDLE);
3023 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3026 /* close file handle */
3027 CloseHandle(pStream->hFile);
3029 /* free allocated space */
3030 HeapFree(GetProcessHeap(), 0, pStream);
3036 /***********************************************************************
3037 * DeleteUrlCacheEntryA (WININET.@)
3040 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3042 URLCACHECONTAINER * pContainer;
3043 LPURLCACHE_HEADER pHeader;
3044 struct _HASH_ENTRY * pHashEntry;
3048 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3050 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3051 if (error != ERROR_SUCCESS)
3053 SetLastError(error);
3057 error = URLCacheContainer_OpenIndex(pContainer);
3058 if (error != ERROR_SUCCESS)
3060 SetLastError(error);
3064 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3067 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3069 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3070 TRACE("entry %s not found!\n", lpszUrlName);
3071 SetLastError(ERROR_FILE_NOT_FOUND);
3075 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3077 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3082 /***********************************************************************
3083 * DeleteUrlCacheEntryW (WININET.@)
3086 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3088 URLCACHECONTAINER * pContainer;
3089 LPURLCACHE_HEADER pHeader;
3090 struct _HASH_ENTRY * pHashEntry;
3095 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3097 urlA = heap_strdupWtoA(lpszUrlName);
3100 SetLastError(ERROR_OUTOFMEMORY);
3104 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3105 if (error != ERROR_SUCCESS)
3107 HeapFree(GetProcessHeap(), 0, urlA);
3108 SetLastError(error);
3112 error = URLCacheContainer_OpenIndex(pContainer);
3113 if (error != ERROR_SUCCESS)
3115 HeapFree(GetProcessHeap(), 0, urlA);
3116 SetLastError(error);
3120 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3122 HeapFree(GetProcessHeap(), 0, urlA);
3126 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3128 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3129 TRACE("entry %s not found!\n", debugstr_a(urlA));
3130 HeapFree(GetProcessHeap(), 0, urlA);
3131 SetLastError(ERROR_FILE_NOT_FOUND);
3135 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3137 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3139 HeapFree(GetProcessHeap(), 0, urlA);
3143 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3145 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3149 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3151 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3155 /***********************************************************************
3156 * CreateCacheContainerA (WININET.@)
3158 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3159 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3161 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3162 d1, d2, d3, d4, d5, d6, d7, d8);
3166 /***********************************************************************
3167 * CreateCacheContainerW (WININET.@)
3169 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3170 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3172 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3173 d1, d2, d3, d4, d5, d6, d7, d8);
3177 /***********************************************************************
3178 * FindFirstUrlCacheContainerA (WININET.@)
3180 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3182 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3186 /***********************************************************************
3187 * FindFirstUrlCacheContainerW (WININET.@)
3189 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3191 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3195 /***********************************************************************
3196 * FindNextUrlCacheContainerA (WININET.@)
3198 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3200 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3204 /***********************************************************************
3205 * FindNextUrlCacheContainerW (WININET.@)
3207 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3209 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3213 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3214 LPCSTR lpszUrlSearchPattern,
3218 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3219 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3221 LPDWORD pcbReserved2,
3225 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3226 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3227 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3228 SetLastError(ERROR_FILE_NOT_FOUND);
3232 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3233 LPCWSTR lpszUrlSearchPattern,
3237 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3238 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3240 LPDWORD pcbReserved2,
3244 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3245 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3246 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3247 SetLastError(ERROR_FILE_NOT_FOUND);
3251 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3253 typedef struct URLCacheFindEntryHandle
3256 LPWSTR lpszUrlSearchPattern;
3257 DWORD dwContainerIndex;
3258 DWORD dwHashTableIndex;
3259 DWORD dwHashEntryIndex;
3260 } URLCacheFindEntryHandle;
3262 /***********************************************************************
3263 * FindFirstUrlCacheEntryA (WININET.@)
3266 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3267 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3269 URLCacheFindEntryHandle *pEntryHandle;
3271 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3273 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3277 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3278 if (lpszUrlSearchPattern)
3280 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3281 if (!pEntryHandle->lpszUrlSearchPattern)
3283 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3288 pEntryHandle->lpszUrlSearchPattern = NULL;
3289 pEntryHandle->dwContainerIndex = 0;
3290 pEntryHandle->dwHashTableIndex = 0;
3291 pEntryHandle->dwHashEntryIndex = 0;
3293 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3295 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3298 return pEntryHandle;
3301 /***********************************************************************
3302 * FindFirstUrlCacheEntryW (WININET.@)
3305 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3306 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3308 URLCacheFindEntryHandle *pEntryHandle;
3310 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3312 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3316 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3317 if (lpszUrlSearchPattern)
3319 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3320 if (!pEntryHandle->lpszUrlSearchPattern)
3322 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3327 pEntryHandle->lpszUrlSearchPattern = NULL;
3328 pEntryHandle->dwContainerIndex = 0;
3329 pEntryHandle->dwHashTableIndex = 0;
3330 pEntryHandle->dwHashEntryIndex = 0;
3332 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3334 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3337 return pEntryHandle;
3340 static BOOL FindNextUrlCacheEntryInternal(
3342 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3343 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3346 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3347 URLCACHECONTAINER * pContainer;
3349 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3351 SetLastError(ERROR_INVALID_HANDLE);
3355 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3356 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3358 LPURLCACHE_HEADER pHeader;
3359 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3362 error = URLCacheContainer_OpenIndex(pContainer);
3363 if (error != ERROR_SUCCESS)
3365 SetLastError(error);
3369 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3372 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3373 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3375 const struct _HASH_ENTRY *pHashEntry = NULL;
3376 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3377 pEntryHandle->dwHashEntryIndex++)
3379 const URL_CACHEFILE_ENTRY *pUrlEntry;
3380 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3382 if (pEntry->dwSignature != URL_SIGNATURE)
3385 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3386 TRACE("Found URL: %s\n",
3387 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3388 TRACE("Header info: %s\n",
3389 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3391 error = URLCache_CopyEntry(
3394 lpNextCacheEntryInfo,
3395 lpdwNextCacheEntryInfoBufferSize,
3398 if (error != ERROR_SUCCESS)
3400 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3401 SetLastError(error);
3404 TRACE("Local File Name: %s\n",
3405 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3407 /* increment the current index so that next time the function
3408 * is called the next entry is returned */
3409 pEntryHandle->dwHashEntryIndex++;
3410 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3415 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3418 SetLastError(ERROR_NO_MORE_ITEMS);
3422 /***********************************************************************
3423 * FindNextUrlCacheEntryA (WININET.@)
3425 BOOL WINAPI FindNextUrlCacheEntryA(
3427 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3428 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3430 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3432 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3433 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3436 /***********************************************************************
3437 * FindNextUrlCacheEntryW (WININET.@)
3439 BOOL WINAPI FindNextUrlCacheEntryW(
3441 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3442 LPDWORD lpdwNextCacheEntryInfoBufferSize
3445 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3447 return FindNextUrlCacheEntryInternal(hEnumHandle,
3448 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3449 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3452 /***********************************************************************
3453 * FindCloseUrlCache (WININET.@)
3455 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3457 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3459 TRACE("(%p)\n", hEnumHandle);
3461 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3463 SetLastError(ERROR_INVALID_HANDLE);
3467 pEntryHandle->dwMagic = 0;
3468 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3469 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3474 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3475 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3477 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3478 dwSearchCondition, lpGroupId, lpReserved);
3482 BOOL WINAPI FindNextUrlCacheEntryExA(
3484 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3485 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3487 LPDWORD pcbReserved2,
3491 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3492 lpReserved, pcbReserved2, lpReserved3);
3496 BOOL WINAPI FindNextUrlCacheEntryExW(
3498 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3499 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3501 LPDWORD pcbReserved2,
3505 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3506 lpReserved, pcbReserved2, lpReserved3);
3510 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3512 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3516 /***********************************************************************
3517 * CreateUrlCacheGroup (WININET.@)
3520 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3522 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3526 /***********************************************************************
3527 * DeleteUrlCacheGroup (WININET.@)
3530 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3532 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3533 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3537 /***********************************************************************
3538 * SetUrlCacheEntryGroupA (WININET.@)
3541 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3542 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3545 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3546 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3547 pbGroupAttributes, cbGroupAttributes, lpReserved);
3548 SetLastError(ERROR_FILE_NOT_FOUND);
3552 /***********************************************************************
3553 * SetUrlCacheEntryGroupW (WININET.@)
3556 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3557 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3560 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3561 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3562 pbGroupAttributes, cbGroupAttributes, lpReserved);
3563 SetLastError(ERROR_FILE_NOT_FOUND);
3567 /***********************************************************************
3568 * GetUrlCacheConfigInfoW (WININET.@)
3570 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3572 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3573 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3577 /***********************************************************************
3578 * GetUrlCacheConfigInfoA (WININET.@)
3580 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3582 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3583 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3587 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3588 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3589 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3591 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3592 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3593 lpdwGroupInfo, lpReserved);
3597 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3598 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3599 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3601 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3602 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3603 lpdwGroupInfo, lpReserved);
3607 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3608 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3610 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3611 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3615 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3616 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3618 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3619 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3623 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3625 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3629 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3631 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3635 /***********************************************************************
3636 * DeleteIE3Cache (WININET.@)
3638 * Deletes the files used by the IE3 URL caching system.
3641 * hWnd [I] A dummy window.
3642 * hInst [I] Instance of process calling the function.
3643 * lpszCmdLine [I] Options used by function.
3644 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3646 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3648 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3652 /***********************************************************************
3653 * IsUrlCacheEntryExpiredA (WININET.@)
3657 * dwFlags [I] Unknown
3658 * pftLastModified [O] Last modified time
3660 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3662 LPURLCACHE_HEADER pHeader;
3663 struct _HASH_ENTRY * pHashEntry;
3664 const CACHEFILE_ENTRY * pEntry;
3665 const URL_CACHEFILE_ENTRY * pUrlEntry;
3666 URLCACHECONTAINER * pContainer;
3669 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3671 error = URLCacheContainers_FindContainerA(url, &pContainer);
3672 if (error != ERROR_SUCCESS)
3674 SetLastError(error);
3678 error = URLCacheContainer_OpenIndex(pContainer);
3679 if (error != ERROR_SUCCESS)
3681 SetLastError(error);
3685 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3688 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3690 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3691 TRACE("entry %s not found!\n", url);
3692 SetLastError(ERROR_FILE_NOT_FOUND);
3696 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3697 if (pEntry->dwSignature != URL_SIGNATURE)
3699 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3700 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3701 SetLastError(ERROR_FILE_NOT_FOUND);
3705 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3707 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3709 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3714 /***********************************************************************
3715 * IsUrlCacheEntryExpiredW (WININET.@)
3719 * dwFlags [I] Unknown
3720 * pftLastModified [O] Last modified time
3722 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3724 LPURLCACHE_HEADER pHeader;
3725 struct _HASH_ENTRY * pHashEntry;
3726 const CACHEFILE_ENTRY * pEntry;
3727 const URL_CACHEFILE_ENTRY * pUrlEntry;
3728 URLCACHECONTAINER * pContainer;
3731 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3733 error = URLCacheContainers_FindContainerW(url, &pContainer);
3734 if (error != ERROR_SUCCESS)
3736 SetLastError(error);
3740 error = URLCacheContainer_OpenIndex(pContainer);
3741 if (error != ERROR_SUCCESS)
3743 SetLastError(error);
3747 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3750 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3752 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3753 TRACE("entry %s not found!\n", debugstr_w(url));
3754 SetLastError(ERROR_FILE_NOT_FOUND);
3758 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3759 if (pEntry->dwSignature != URL_SIGNATURE)
3761 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3762 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3763 SetLastError(ERROR_FILE_NOT_FOUND);
3767 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3769 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3771 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3776 /***********************************************************************
3777 * GetDiskInfoA (WININET.@)
3779 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3782 ULARGE_INTEGER bytes_free, bytes_total;
3784 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3788 SetLastError(ERROR_INVALID_PARAMETER);
3792 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3794 if (cluster_size) *cluster_size = 1;
3795 if (free) *free = bytes_free.QuadPart;
3796 if (total) *total = bytes_total.QuadPart;
3801 /***********************************************************************
3802 * RegisterUrlCacheNotification (WININET.@)
3804 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3806 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3810 /***********************************************************************
3811 * IncrementUrlCacheHeaderData (WININET.@)
3813 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3815 FIXME("(%u, %p)\n", index, data);