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 /***********************************************************************
2043 * UnlockUrlCacheEntryFileA (WININET.@)
2046 BOOL WINAPI UnlockUrlCacheEntryFileA(
2047 IN LPCSTR lpszUrlName,
2051 LPURLCACHE_HEADER pHeader;
2052 struct _HASH_ENTRY * pHashEntry;
2053 CACHEFILE_ENTRY * pEntry;
2054 URL_CACHEFILE_ENTRY * pUrlEntry;
2055 URLCACHECONTAINER * pContainer;
2058 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2062 ERR("dwReserved != 0\n");
2063 SetLastError(ERROR_INVALID_PARAMETER);
2067 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2068 if (error != ERROR_SUCCESS)
2070 SetLastError(error);
2074 error = URLCacheContainer_OpenIndex(pContainer);
2075 if (error != ERROR_SUCCESS)
2077 SetLastError(error);
2081 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2084 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2086 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2087 TRACE("entry %s not found!\n", lpszUrlName);
2088 SetLastError(ERROR_FILE_NOT_FOUND);
2092 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2093 if (pEntry->dwSignature != URL_SIGNATURE)
2095 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2096 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2097 SetLastError(ERROR_FILE_NOT_FOUND);
2101 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2103 if (pUrlEntry->dwUseCount == 0)
2105 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2108 pUrlEntry->dwUseCount--;
2109 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2111 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2116 /***********************************************************************
2117 * UnlockUrlCacheEntryFileW (WININET.@)
2120 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2122 LPURLCACHE_HEADER pHeader;
2123 struct _HASH_ENTRY * pHashEntry;
2124 CACHEFILE_ENTRY * pEntry;
2125 URL_CACHEFILE_ENTRY * pUrlEntry;
2126 URLCACHECONTAINER * pContainer;
2129 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2133 ERR("dwReserved != 0\n");
2134 SetLastError(ERROR_INVALID_PARAMETER);
2138 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2139 if (error != ERROR_SUCCESS)
2141 SetLastError(error);
2145 error = URLCacheContainer_OpenIndex(pContainer);
2146 if (error != ERROR_SUCCESS)
2148 SetLastError(error);
2152 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2155 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2157 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2158 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2159 SetLastError(ERROR_FILE_NOT_FOUND);
2163 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2164 if (pEntry->dwSignature != URL_SIGNATURE)
2166 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2167 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2168 SetLastError(ERROR_FILE_NOT_FOUND);
2172 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2174 if (pUrlEntry->dwUseCount == 0)
2176 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2179 pUrlEntry->dwUseCount--;
2180 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2182 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2187 /***********************************************************************
2188 * CreateUrlCacheEntryA (WININET.@)
2191 BOOL WINAPI CreateUrlCacheEntryA(
2192 IN LPCSTR lpszUrlName,
2193 IN DWORD dwExpectedFileSize,
2194 IN LPCSTR lpszFileExtension,
2195 OUT LPSTR lpszFileName,
2200 WCHAR *file_extension = NULL;
2201 WCHAR file_name[MAX_PATH];
2202 BOOL bSuccess = FALSE;
2205 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2206 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2208 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2210 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2212 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2214 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2220 dwError = GetLastError();
2225 dwError = GetLastError();
2227 HeapFree(GetProcessHeap(), 0, file_extension);
2231 dwError = GetLastError();
2233 HeapFree(GetProcessHeap(), 0, url_name);
2235 SetLastError(dwError);
2239 /***********************************************************************
2240 * CreateUrlCacheEntryW (WININET.@)
2243 BOOL WINAPI CreateUrlCacheEntryW(
2244 IN LPCWSTR lpszUrlName,
2245 IN DWORD dwExpectedFileSize,
2246 IN LPCWSTR lpszFileExtension,
2247 OUT LPWSTR lpszFileName,
2251 URLCACHECONTAINER * pContainer;
2252 LPURLCACHE_HEADER pHeader;
2253 CHAR szFile[MAX_PATH];
2254 WCHAR szExtension[MAX_PATH];
2255 LPCWSTR lpszUrlPart;
2257 LPCWSTR lpszFileNameExtension;
2258 LPWSTR lpszFileNameNoPath;
2260 int countnoextension;
2263 BOOL bFound = FALSE;
2269 static const WCHAR szWWW[] = {'w','w','w',0};
2270 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2272 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2273 debugstr_w(lpszUrlName),
2275 debugstr_w(lpszFileExtension),
2280 FIXME("dwReserved 0x%08x\n", dwReserved);
2282 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2284 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2287 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2289 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2291 lpszUrlEnd = lpszUrlPart;
2293 for (lpszUrlPart = lpszUrlEnd;
2294 (lpszUrlPart >= lpszUrlName);
2297 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2304 if (!lstrcmpW(lpszUrlPart, szWWW))
2306 lpszUrlPart += lstrlenW(szWWW);
2309 count = lpszUrlEnd - lpszUrlPart;
2311 if (bFound && (count < MAX_PATH))
2313 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2317 while(len && szFile[--len] == '/') szFile[len] = '\0';
2319 /* FIXME: get rid of illegal characters like \, / and : */
2323 FIXME("need to generate a random filename\n");
2326 TRACE("File name: %s\n", debugstr_a(szFile));
2328 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2329 if (error != ERROR_SUCCESS)
2331 SetLastError(error);
2335 error = URLCacheContainer_OpenIndex(pContainer);
2336 if (error != ERROR_SUCCESS)
2338 SetLastError(error);
2342 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2345 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2347 lBufferSize = MAX_PATH * sizeof(WCHAR);
2348 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2350 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2351 debugstr_a(szFile), lBufferSize);
2352 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2356 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2358 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2359 lpszFileNameNoPath >= lpszFileName;
2360 --lpszFileNameNoPath)
2362 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2366 countnoextension = lstrlenW(lpszFileNameNoPath);
2367 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2368 if (lpszFileNameExtension)
2369 countnoextension -= lstrlenW(lpszFileNameExtension);
2370 *szExtension = '\0';
2372 if (lpszFileExtension)
2374 szExtension[0] = '.';
2375 lstrcpyW(szExtension+1, lpszFileExtension);
2378 for (i = 0; i < 255; i++)
2380 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2383 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2384 for (p = lpszFileNameNoPath + 1; *p; p++)
2390 case '/': case '\\':
2397 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2399 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2400 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2401 if (hFile != INVALID_HANDLE_VALUE)
2408 GetSystemTimeAsFileTime(&ft);
2409 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2411 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2412 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2413 if (hFile != INVALID_HANDLE_VALUE)
2419 WARN("Could not find a unique filename\n");
2424 /***********************************************************************
2425 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2427 * The bug we are compensating for is that some drongo at Microsoft
2428 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2429 * As a consequence, CommitUrlCacheEntryA has been effectively
2430 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2431 * is still defined as LPCWSTR. The result (other than madness) is
2432 * that we always need to store lpHeaderInfo in CP_ACP rather than
2433 * in UTF16, and we need to avoid converting lpHeaderInfo in
2434 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2435 * result will lose data for arbitrary binary data.
2438 static BOOL CommitUrlCacheEntryInternal(
2439 IN LPCWSTR lpszUrlName,
2440 IN LPCWSTR lpszLocalFileName,
2441 IN FILETIME ExpireTime,
2442 IN FILETIME LastModifiedTime,
2443 IN DWORD CacheEntryType,
2444 IN LPBYTE lpHeaderInfo,
2445 IN DWORD dwHeaderSize,
2446 IN LPCWSTR lpszFileExtension,
2447 IN LPCWSTR lpszOriginalUrl
2450 URLCACHECONTAINER * pContainer;
2451 LPURLCACHE_HEADER pHeader;
2452 struct _HASH_ENTRY * pHashEntry;
2453 CACHEFILE_ENTRY * pEntry;
2454 URL_CACHEFILE_ENTRY * pUrlEntry;
2455 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2456 DWORD dwOffsetLocalFileName = 0;
2457 DWORD dwOffsetHeader = 0;
2458 DWORD dwOffsetFileExtension = 0;
2459 LARGE_INTEGER file_size;
2460 BYTE cDirectory = 0;
2461 char achFile[MAX_PATH];
2462 LPSTR lpszUrlNameA = NULL;
2463 LPSTR lpszFileExtensionA = NULL;
2464 char *pchLocalFileName = 0;
2467 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2468 debugstr_w(lpszUrlName),
2469 debugstr_w(lpszLocalFileName),
2473 debugstr_w(lpszFileExtension),
2474 debugstr_w(lpszOriginalUrl));
2476 if (lpszOriginalUrl)
2477 WARN(": lpszOriginalUrl ignored\n");
2479 file_size.QuadPart = 0;
2480 if (lpszLocalFileName)
2484 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2485 if (hFile == INVALID_HANDLE_VALUE)
2487 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2492 if (!GetFileSizeEx(hFile, &file_size))
2494 ERR("couldn't get file size (error is %d)\n", GetLastError());
2502 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2503 if (error != ERROR_SUCCESS)
2505 SetLastError(error);
2509 error = URLCacheContainer_OpenIndex(pContainer);
2510 if (error != ERROR_SUCCESS)
2512 SetLastError(error);
2516 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2519 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2522 error = GetLastError();
2526 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2528 error = GetLastError();
2532 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2534 FIXME("entry already in cache - don't know what to do!\n");
2536 * SetLastError(ERROR_FILE_NOT_FOUND);
2542 if (lpszLocalFileName)
2544 BOOL bFound = FALSE;
2546 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2548 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2549 error = ERROR_INVALID_PARAMETER;
2553 /* skip container path prefix */
2554 lpszLocalFileName += lstrlenW(pContainer->path);
2556 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2557 pchLocalFileName = achFile;
2559 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2561 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2570 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2571 error = ERROR_INVALID_PARAMETER;
2575 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2578 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2579 if (lpszLocalFileName)
2581 dwOffsetLocalFileName = dwBytesNeeded;
2582 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2586 dwOffsetHeader = dwBytesNeeded;
2587 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2589 if (lpszFileExtensionA)
2591 dwOffsetFileExtension = dwBytesNeeded;
2592 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2595 /* round up to next block */
2596 if (dwBytesNeeded % BLOCKSIZE)
2598 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2599 dwBytesNeeded += BLOCKSIZE;
2602 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2604 ERR("no free entries\n");
2605 error = ERROR_DISK_FULL;
2609 /* FindFirstFreeEntry fills in blocks used */
2610 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2611 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2612 pUrlEntry->CacheDir = cDirectory;
2613 pUrlEntry->CacheEntryType = CacheEntryType;
2614 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2615 pUrlEntry->dwExemptDelta = 0;
2616 pUrlEntry->dwHitRate = 0;
2617 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2618 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2619 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2620 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2621 pUrlEntry->size.QuadPart = file_size.QuadPart;
2622 pUrlEntry->dwUseCount = 0;
2623 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2624 pUrlEntry->LastModifiedTime = LastModifiedTime;
2625 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2626 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2627 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2628 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2631 pUrlEntry->dwUnknown1 = 0;
2632 pUrlEntry->dwUnknown2 = 0;
2633 pUrlEntry->dwUnknown3 = 0x60;
2634 pUrlEntry->Unknown4 = 0;
2635 pUrlEntry->wUnknown5 = 0x1010;
2636 pUrlEntry->dwUnknown7 = 0;
2637 pUrlEntry->dwUnknown8 = 0;
2640 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2641 if (dwOffsetLocalFileName)
2642 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2644 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2645 if (dwOffsetFileExtension)
2646 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2648 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2649 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2650 if (error != ERROR_SUCCESS)
2651 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2654 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2655 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2656 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2657 if (pHeader->CacheUsage.QuadPart > pHeader->CacheLimit.QuadPart)
2658 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2662 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2663 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2664 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2666 if (error == ERROR_SUCCESS)
2670 SetLastError(error);
2675 /***********************************************************************
2676 * CommitUrlCacheEntryA (WININET.@)
2679 BOOL WINAPI CommitUrlCacheEntryA(
2680 IN LPCSTR lpszUrlName,
2681 IN LPCSTR lpszLocalFileName,
2682 IN FILETIME ExpireTime,
2683 IN FILETIME LastModifiedTime,
2684 IN DWORD CacheEntryType,
2685 IN LPBYTE lpHeaderInfo,
2686 IN DWORD dwHeaderSize,
2687 IN LPCSTR lpszFileExtension,
2688 IN LPCSTR lpszOriginalUrl
2691 WCHAR *url_name = NULL;
2692 WCHAR *local_file_name = NULL;
2693 WCHAR *original_url = NULL;
2694 WCHAR *file_extension = NULL;
2695 BOOL bSuccess = FALSE;
2697 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2698 debugstr_a(lpszUrlName),
2699 debugstr_a(lpszLocalFileName),
2703 debugstr_a(lpszFileExtension),
2704 debugstr_a(lpszOriginalUrl));
2706 url_name = heap_strdupAtoW(lpszUrlName);
2710 if (lpszLocalFileName)
2712 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2713 if (!local_file_name)
2716 if (lpszFileExtension)
2718 file_extension = heap_strdupAtoW(lpszFileExtension);
2719 if (!file_extension)
2722 if (lpszOriginalUrl)
2724 original_url = heap_strdupAtoW(lpszOriginalUrl);
2729 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2730 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2731 file_extension, original_url);
2734 HeapFree(GetProcessHeap(), 0, original_url);
2735 HeapFree(GetProcessHeap(), 0, file_extension);
2736 HeapFree(GetProcessHeap(), 0, local_file_name);
2737 HeapFree(GetProcessHeap(), 0, url_name);
2742 /***********************************************************************
2743 * CommitUrlCacheEntryW (WININET.@)
2746 BOOL WINAPI CommitUrlCacheEntryW(
2747 IN LPCWSTR lpszUrlName,
2748 IN LPCWSTR lpszLocalFileName,
2749 IN FILETIME ExpireTime,
2750 IN FILETIME LastModifiedTime,
2751 IN DWORD CacheEntryType,
2752 IN LPWSTR lpHeaderInfo,
2753 IN DWORD dwHeaderSize,
2754 IN LPCWSTR lpszFileExtension,
2755 IN LPCWSTR lpszOriginalUrl
2759 BOOL bSuccess = FALSE;
2761 CHAR *header_info = NULL;
2763 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2764 debugstr_w(lpszUrlName),
2765 debugstr_w(lpszLocalFileName),
2769 debugstr_w(lpszFileExtension),
2770 debugstr_w(lpszOriginalUrl));
2772 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2774 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2775 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2781 dwError = GetLastError();
2785 HeapFree(GetProcessHeap(), 0, header_info);
2787 SetLastError(dwError);
2793 /***********************************************************************
2794 * ReadUrlCacheEntryStream (WININET.@)
2797 BOOL WINAPI ReadUrlCacheEntryStream(
2798 IN HANDLE hUrlCacheStream,
2799 IN DWORD dwLocation,
2800 IN OUT LPVOID lpBuffer,
2801 IN OUT LPDWORD lpdwLen,
2805 /* Get handle to file from 'stream' */
2806 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2808 if (dwReserved != 0)
2810 ERR("dwReserved != 0\n");
2811 SetLastError(ERROR_INVALID_PARAMETER);
2815 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2817 SetLastError(ERROR_INVALID_HANDLE);
2821 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2823 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2826 /***********************************************************************
2827 * RetrieveUrlCacheEntryStreamA (WININET.@)
2830 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2831 IN LPCSTR lpszUrlName,
2832 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2833 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2834 IN BOOL fRandomRead,
2838 /* NOTE: this is not the same as the way that the native
2839 * version allocates 'stream' handles. I did it this way
2840 * as it is much easier and no applications should depend
2841 * on this behaviour. (Native version appears to allocate
2842 * indices into a table)
2844 STREAM_HANDLE * pStream;
2847 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2848 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2850 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2852 lpdwCacheEntryInfoBufferSize,
2858 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2863 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2865 if (hFile == INVALID_HANDLE_VALUE)
2868 /* allocate handle storage space */
2869 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2873 SetLastError(ERROR_OUTOFMEMORY);
2877 pStream->hFile = hFile;
2878 strcpy(pStream->lpszUrl, lpszUrlName);
2882 /***********************************************************************
2883 * RetrieveUrlCacheEntryStreamW (WININET.@)
2886 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2887 IN LPCWSTR lpszUrlName,
2888 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2889 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2890 IN BOOL fRandomRead,
2896 /* NOTE: this is not the same as the way that the native
2897 * version allocates 'stream' handles. I did it this way
2898 * as it is much easier and no applications should depend
2899 * on this behaviour. (Native version appears to allocate
2900 * indices into a table)
2902 STREAM_HANDLE * pStream;
2905 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2906 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2908 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
2910 lpdwCacheEntryInfoBufferSize,
2916 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
2921 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2923 if (hFile == INVALID_HANDLE_VALUE)
2926 /* allocate handle storage space */
2927 size = sizeof(STREAM_HANDLE);
2928 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2930 pStream = HeapAlloc(GetProcessHeap(), 0, size);
2934 SetLastError(ERROR_OUTOFMEMORY);
2938 pStream->hFile = hFile;
2939 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
2943 /***********************************************************************
2944 * UnlockUrlCacheEntryStream (WININET.@)
2947 BOOL WINAPI UnlockUrlCacheEntryStream(
2948 IN HANDLE hUrlCacheStream,
2952 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2954 if (dwReserved != 0)
2956 ERR("dwReserved != 0\n");
2957 SetLastError(ERROR_INVALID_PARAMETER);
2961 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2963 SetLastError(ERROR_INVALID_HANDLE);
2967 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2970 /* close file handle */
2971 CloseHandle(pStream->hFile);
2973 /* free allocated space */
2974 HeapFree(GetProcessHeap(), 0, pStream);
2980 /***********************************************************************
2981 * DeleteUrlCacheEntryA (WININET.@)
2984 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2986 URLCACHECONTAINER * pContainer;
2987 LPURLCACHE_HEADER pHeader;
2988 struct _HASH_ENTRY * pHashEntry;
2989 CACHEFILE_ENTRY * pEntry;
2990 const URL_CACHEFILE_ENTRY * pUrlEntry;
2993 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2995 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2996 if (error != ERROR_SUCCESS)
2998 SetLastError(error);
3002 error = URLCacheContainer_OpenIndex(pContainer);
3003 if (error != ERROR_SUCCESS)
3005 SetLastError(error);
3009 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3012 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3014 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3015 TRACE("entry %s not found!\n", lpszUrlName);
3016 SetLastError(ERROR_FILE_NOT_FOUND);
3020 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3021 if (pEntry->dwSignature != URL_SIGNATURE)
3023 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3024 FIXME("Trying to delete entry of unknown format %s\n",
3025 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3026 SetLastError(ERROR_FILE_NOT_FOUND);
3029 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3030 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3032 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
3033 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
3035 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
3036 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
3038 pHeader->CacheUsage.QuadPart = 0;
3040 URLCache_DeleteEntry(pHeader, pEntry);
3042 URLCache_DeleteEntryFromHash(pHashEntry);
3044 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3049 /***********************************************************************
3050 * DeleteUrlCacheEntryW (WININET.@)
3053 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3055 URLCACHECONTAINER * pContainer;
3056 LPURLCACHE_HEADER pHeader;
3057 struct _HASH_ENTRY * pHashEntry;
3058 CACHEFILE_ENTRY * pEntry;
3059 const URL_CACHEFILE_ENTRY * pUrlEntry;
3063 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3065 urlA = heap_strdupWtoA(lpszUrlName);
3068 SetLastError(ERROR_OUTOFMEMORY);
3072 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3073 if (error != ERROR_SUCCESS)
3075 HeapFree(GetProcessHeap(), 0, urlA);
3076 SetLastError(error);
3080 error = URLCacheContainer_OpenIndex(pContainer);
3081 if (error != ERROR_SUCCESS)
3083 HeapFree(GetProcessHeap(), 0, urlA);
3084 SetLastError(error);
3088 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3090 HeapFree(GetProcessHeap(), 0, urlA);
3094 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3096 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3097 TRACE("entry %s not found!\n", debugstr_a(urlA));
3098 HeapFree(GetProcessHeap(), 0, urlA);
3099 SetLastError(ERROR_FILE_NOT_FOUND);
3103 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3104 if (pEntry->dwSignature != URL_SIGNATURE)
3106 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3107 FIXME("Trying to delete entry of unknown format %s\n",
3108 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3109 SetLastError(ERROR_FILE_NOT_FOUND);
3112 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3113 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3115 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
3116 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
3118 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
3119 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
3121 pHeader->CacheUsage.QuadPart = 0;
3123 URLCache_DeleteEntry(pHeader, pEntry);
3125 URLCache_DeleteEntryFromHash(pHashEntry);
3127 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3129 HeapFree(GetProcessHeap(), 0, urlA);
3133 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3135 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3139 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3141 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3145 /***********************************************************************
3146 * CreateCacheContainerA (WININET.@)
3148 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3149 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3151 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3152 d1, d2, d3, d4, d5, d6, d7, d8);
3156 /***********************************************************************
3157 * CreateCacheContainerW (WININET.@)
3159 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3160 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3162 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3163 d1, d2, d3, d4, d5, d6, d7, d8);
3167 /***********************************************************************
3168 * FindFirstUrlCacheContainerA (WININET.@)
3170 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3172 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3176 /***********************************************************************
3177 * FindFirstUrlCacheContainerW (WININET.@)
3179 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3181 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3185 /***********************************************************************
3186 * FindNextUrlCacheContainerA (WININET.@)
3188 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3190 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3194 /***********************************************************************
3195 * FindNextUrlCacheContainerW (WININET.@)
3197 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3199 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3203 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3204 LPCSTR lpszUrlSearchPattern,
3208 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3209 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3211 LPDWORD pcbReserved2,
3215 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3216 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3217 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3218 SetLastError(ERROR_FILE_NOT_FOUND);
3222 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3223 LPCWSTR lpszUrlSearchPattern,
3227 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3228 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3230 LPDWORD pcbReserved2,
3234 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3235 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3236 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3237 SetLastError(ERROR_FILE_NOT_FOUND);
3241 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3243 typedef struct URLCacheFindEntryHandle
3246 LPWSTR lpszUrlSearchPattern;
3247 DWORD dwContainerIndex;
3248 DWORD dwHashTableIndex;
3249 DWORD dwHashEntryIndex;
3250 } URLCacheFindEntryHandle;
3252 /***********************************************************************
3253 * FindFirstUrlCacheEntryA (WININET.@)
3256 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3257 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3259 URLCacheFindEntryHandle *pEntryHandle;
3261 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3263 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3267 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3268 if (lpszUrlSearchPattern)
3270 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3271 if (!pEntryHandle->lpszUrlSearchPattern)
3273 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3278 pEntryHandle->lpszUrlSearchPattern = NULL;
3279 pEntryHandle->dwContainerIndex = 0;
3280 pEntryHandle->dwHashTableIndex = 0;
3281 pEntryHandle->dwHashEntryIndex = 0;
3283 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3285 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3288 return pEntryHandle;
3291 /***********************************************************************
3292 * FindFirstUrlCacheEntryW (WININET.@)
3295 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3296 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3298 URLCacheFindEntryHandle *pEntryHandle;
3300 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3302 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3306 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3307 if (lpszUrlSearchPattern)
3309 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3310 if (!pEntryHandle->lpszUrlSearchPattern)
3312 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3317 pEntryHandle->lpszUrlSearchPattern = NULL;
3318 pEntryHandle->dwContainerIndex = 0;
3319 pEntryHandle->dwHashTableIndex = 0;
3320 pEntryHandle->dwHashEntryIndex = 0;
3322 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3324 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3327 return pEntryHandle;
3330 static BOOL FindNextUrlCacheEntryInternal(
3332 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3333 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3336 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3337 URLCACHECONTAINER * pContainer;
3339 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3341 SetLastError(ERROR_INVALID_HANDLE);
3345 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3346 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3348 LPURLCACHE_HEADER pHeader;
3349 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3352 error = URLCacheContainer_OpenIndex(pContainer);
3353 if (error != ERROR_SUCCESS)
3355 SetLastError(error);
3359 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3362 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3363 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3365 const struct _HASH_ENTRY *pHashEntry = NULL;
3366 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3367 pEntryHandle->dwHashEntryIndex++)
3369 const URL_CACHEFILE_ENTRY *pUrlEntry;
3370 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3372 if (pEntry->dwSignature != URL_SIGNATURE)
3375 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3376 TRACE("Found URL: %s\n",
3377 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3378 TRACE("Header info: %s\n",
3379 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3381 error = URLCache_CopyEntry(
3384 lpNextCacheEntryInfo,
3385 lpdwNextCacheEntryInfoBufferSize,
3388 if (error != ERROR_SUCCESS)
3390 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3391 SetLastError(error);
3394 TRACE("Local File Name: %s\n",
3395 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3397 /* increment the current index so that next time the function
3398 * is called the next entry is returned */
3399 pEntryHandle->dwHashEntryIndex++;
3400 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3405 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3408 SetLastError(ERROR_NO_MORE_ITEMS);
3412 /***********************************************************************
3413 * FindNextUrlCacheEntryA (WININET.@)
3415 BOOL WINAPI FindNextUrlCacheEntryA(
3417 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3418 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3420 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3422 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3423 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3426 /***********************************************************************
3427 * FindNextUrlCacheEntryW (WININET.@)
3429 BOOL WINAPI FindNextUrlCacheEntryW(
3431 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3432 LPDWORD lpdwNextCacheEntryInfoBufferSize
3435 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3437 return FindNextUrlCacheEntryInternal(hEnumHandle,
3438 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3439 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3442 /***********************************************************************
3443 * FindCloseUrlCache (WININET.@)
3445 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3447 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3449 TRACE("(%p)\n", hEnumHandle);
3451 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3453 SetLastError(ERROR_INVALID_HANDLE);
3457 pEntryHandle->dwMagic = 0;
3458 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3459 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3464 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3465 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3467 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3468 dwSearchCondition, lpGroupId, lpReserved);
3472 BOOL WINAPI FindNextUrlCacheEntryExA(
3474 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3475 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3477 LPDWORD pcbReserved2,
3481 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3482 lpReserved, pcbReserved2, lpReserved3);
3486 BOOL WINAPI FindNextUrlCacheEntryExW(
3488 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3489 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3491 LPDWORD pcbReserved2,
3495 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3496 lpReserved, pcbReserved2, lpReserved3);
3500 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3502 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3506 /***********************************************************************
3507 * CreateUrlCacheGroup (WININET.@)
3510 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3512 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3516 /***********************************************************************
3517 * DeleteUrlCacheGroup (WININET.@)
3520 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3522 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3523 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3527 /***********************************************************************
3528 * SetUrlCacheEntryGroupA (WININET.@)
3531 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3532 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3535 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3536 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3537 pbGroupAttributes, cbGroupAttributes, lpReserved);
3538 SetLastError(ERROR_FILE_NOT_FOUND);
3542 /***********************************************************************
3543 * SetUrlCacheEntryGroupW (WININET.@)
3546 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3547 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3550 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3551 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3552 pbGroupAttributes, cbGroupAttributes, lpReserved);
3553 SetLastError(ERROR_FILE_NOT_FOUND);
3557 /***********************************************************************
3558 * GetUrlCacheConfigInfoW (WININET.@)
3560 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3562 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3563 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3567 /***********************************************************************
3568 * GetUrlCacheConfigInfoA (WININET.@)
3570 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3572 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3573 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3577 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3578 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3579 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3581 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3582 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3583 lpdwGroupInfo, lpReserved);
3587 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3588 LPINTERNET_CACHE_GROUP_INFOW 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 SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3598 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3600 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3601 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3605 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3606 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3608 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3609 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3613 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3615 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3619 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3621 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3625 /***********************************************************************
3626 * DeleteIE3Cache (WININET.@)
3628 * Deletes the files used by the IE3 URL caching system.
3631 * hWnd [I] A dummy window.
3632 * hInst [I] Instance of process calling the function.
3633 * lpszCmdLine [I] Options used by function.
3634 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3636 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3638 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3642 /***********************************************************************
3643 * IsUrlCacheEntryExpiredA (WININET.@)
3647 * dwFlags [I] Unknown
3648 * pftLastModified [O] Last modified time
3650 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3652 LPURLCACHE_HEADER pHeader;
3653 struct _HASH_ENTRY * pHashEntry;
3654 const CACHEFILE_ENTRY * pEntry;
3655 const URL_CACHEFILE_ENTRY * pUrlEntry;
3656 URLCACHECONTAINER * pContainer;
3659 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3661 error = URLCacheContainers_FindContainerA(url, &pContainer);
3662 if (error != ERROR_SUCCESS)
3664 SetLastError(error);
3668 error = URLCacheContainer_OpenIndex(pContainer);
3669 if (error != ERROR_SUCCESS)
3671 SetLastError(error);
3675 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3678 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3680 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3681 TRACE("entry %s not found!\n", url);
3682 SetLastError(ERROR_FILE_NOT_FOUND);
3686 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3687 if (pEntry->dwSignature != URL_SIGNATURE)
3689 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3690 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3691 SetLastError(ERROR_FILE_NOT_FOUND);
3695 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3697 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3699 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3704 /***********************************************************************
3705 * IsUrlCacheEntryExpiredW (WININET.@)
3709 * dwFlags [I] Unknown
3710 * pftLastModified [O] Last modified time
3712 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3714 LPURLCACHE_HEADER pHeader;
3715 struct _HASH_ENTRY * pHashEntry;
3716 const CACHEFILE_ENTRY * pEntry;
3717 const URL_CACHEFILE_ENTRY * pUrlEntry;
3718 URLCACHECONTAINER * pContainer;
3721 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3723 error = URLCacheContainers_FindContainerW(url, &pContainer);
3724 if (error != ERROR_SUCCESS)
3726 SetLastError(error);
3730 error = URLCacheContainer_OpenIndex(pContainer);
3731 if (error != ERROR_SUCCESS)
3733 SetLastError(error);
3737 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3740 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3742 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3743 TRACE("entry %s not found!\n", debugstr_w(url));
3744 SetLastError(ERROR_FILE_NOT_FOUND);
3748 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3749 if (pEntry->dwSignature != URL_SIGNATURE)
3751 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3752 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3753 SetLastError(ERROR_FILE_NOT_FOUND);
3757 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3759 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3761 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3766 /***********************************************************************
3767 * GetDiskInfoA (WININET.@)
3769 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3772 ULARGE_INTEGER bytes_free, bytes_total;
3774 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3778 SetLastError(ERROR_INVALID_PARAMETER);
3782 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3784 if (cluster_size) *cluster_size = 1;
3785 if (free) *free = bytes_free.QuadPart;
3786 if (total) *total = bytes_total.QuadPart;
3791 /***********************************************************************
3792 * RegisterUrlCacheNotification (WININET.@)
3794 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3796 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3800 /***********************************************************************
3801 * IncrementUrlCacheHeaderData (WININET.@)
3803 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3805 FIXME("(%u, %p)\n", index, data);