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 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
100 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
101 DWORD dwUnknown2; /* usually zero */
102 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
103 DWORD dwUnknown3; /* usually 0x60 */
104 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
105 BYTE CacheDir; /* index of cache directory this url is stored in */
106 BYTE Unknown4; /* usually zero */
107 WORD wUnknown5; /* usually 0x1010 */
108 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
109 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
110 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
111 DWORD dwHeaderInfoSize;
112 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
113 WORD wLastSyncDate; /* last sync date in dos format */
114 WORD wLastSyncTime; /* last sync time in dos format */
115 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
116 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
117 WORD wUnknownDate; /* usually same as wLastSyncDate */
118 WORD wUnknownTime; /* usually same as wLastSyncTime */
119 DWORD dwUnknown7; /* usually zero */
120 DWORD dwUnknown8; /* usually zero */
121 /* packing to dword align start of next field */
122 /* CHAR szSourceUrlName[]; (url) */
123 /* packing to dword align start of next field */
124 /* CHAR szLocalFileName[]; (local file name excluding path) */
125 /* packing to dword align start of next field */
126 /* CHAR szHeaderInfo[]; (header info) */
127 } URL_CACHEFILE_ENTRY;
135 typedef struct _HASH_CACHEFILE_ENTRY
137 CACHEFILE_ENTRY CacheFileEntry;
139 DWORD dwHashTableNumber;
140 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
141 } HASH_CACHEFILE_ENTRY;
143 typedef struct _DIRECTORY_DATA
146 char filename[DIR_LENGTH];
149 typedef struct _URLCACHE_HEADER
151 char szSignature[28];
153 DWORD dwOffsetFirstHashTable;
154 DWORD dwIndexCapacityInBlocks;
157 DWORD dwCacheLimitLow; /* disk space limit for cache */
158 DWORD dwCacheLimitHigh; /* disk space limit for cache */
159 DWORD dwUnknown4; /* current disk space usage for cache */
160 DWORD dwUnknown5; /* current disk space usage for cache */
161 DWORD dwUnknown6; /* possibly a flag? */
163 BYTE DirectoryCount; /* number of directory_data's */
164 BYTE Unknown8[3]; /* just padding? */
165 DIRECTORY_DATA directory_data[1]; /* first directory entry */
166 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
167 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
169 typedef struct _STREAM_HANDLE
175 typedef struct _URLCACHECONTAINER
177 struct list entry; /* part of a list */
178 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
179 LPWSTR path; /* path to url container directory */
180 HANDLE hMapping; /* handle of file mapping */
181 DWORD file_size; /* size of file when mapping was opened */
182 HANDLE hMutex; /* handle of mutex */
186 /* List of all containers available */
187 static struct list UrlContainers = LIST_INIT(UrlContainers);
189 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
191 /***********************************************************************
192 * URLCache_PathToObjectName (Internal)
194 * Converts a path to a name suitable for use as a Win32 object name.
195 * Replaces '\\' characters in-place with the specified character
196 * (usually '_' or '!')
202 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
204 for (; *lpszPath; lpszPath++)
206 if (*lpszPath == '\\')
211 /***********************************************************************
212 * URLCacheContainer_OpenIndex (Internal)
214 * Opens the index file and saves mapping handle in hCacheIndexMapping
217 * ERROR_SUCCESS if succeeded
218 * Any other Win32 error code if failed
221 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
224 WCHAR wszFilePath[MAX_PATH];
227 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
228 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
230 if (pContainer->hMapping)
231 return ERROR_SUCCESS;
233 strcpyW(wszFilePath, pContainer->path);
234 strcatW(wszFilePath, wszIndex);
236 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
237 if (hFile == INVALID_HANDLE_VALUE)
239 /* Maybe the directory wasn't there? Try to create it */
240 if (CreateDirectoryW(pContainer->path, 0))
241 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
243 if (hFile == INVALID_HANDLE_VALUE)
245 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
246 return GetLastError();
249 /* At this stage we need the mutex because we may be about to create the
252 WaitForSingleObject(pContainer->hMutex, INFINITE);
254 dwFileSize = GetFileSize(hFile, NULL);
255 if (dwFileSize == INVALID_FILE_SIZE)
257 ReleaseMutex(pContainer->hMutex);
258 return GetLastError();
263 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
265 char achZeroes[0x1000];
267 DWORD dwError = ERROR_SUCCESS;
269 /* Write zeroes to the entire file so we can safely map it without
270 * fear of getting a SEGV because the disk is full.
272 memset(achZeroes, 0, sizeof(achZeroes));
273 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
275 DWORD dwWrite = sizeof(achZeroes);
278 if (NEWFILE_SIZE - dwOffset < dwWrite)
279 dwWrite = NEWFILE_SIZE - dwOffset;
280 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
281 dwWritten != dwWrite)
283 /* If we fail to write, we need to return the error that
284 * cause the problem and also make sure the file is no
285 * longer there, if possible.
287 dwError = GetLastError();
293 if (dwError == ERROR_SUCCESS)
295 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
299 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
304 WCHAR wszDirPath[MAX_PATH];
307 HASH_CACHEFILE_ENTRY *pHashEntry;
309 dwFileSize = NEWFILE_SIZE;
311 /* First set some constants and defaults in the header */
312 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
313 pHeader->dwFileSize = dwFileSize;
314 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
315 /* 127MB - taken from default for Windows 2000 */
316 pHeader->dwCacheLimitHigh = 0;
317 pHeader->dwCacheLimitLow = 0x07ff5400;
318 /* Copied from a Windows 2000 cache index */
319 pHeader->DirectoryCount = 4;
321 /* If the registry has a cache size set, use the registry value */
322 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
325 DWORD len = sizeof(dw);
328 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
329 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
330 keytype == REG_DWORD)
332 pHeader->dwCacheLimitHigh = (dw >> 22);
333 pHeader->dwCacheLimitLow = dw << 10;
338 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
340 /* Last step - create the directories */
342 strcpyW(wszDirPath, pContainer->path);
343 pwchDir = wszDirPath + strlenW(wszDirPath);
346 GetSystemTimeAsFileTime(&ft);
348 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
350 /* The following values were copied from a Windows index.
351 * I don't know what the values are supposed to mean but
352 * have made them the same in the hope that this will
353 * be better for compatibility
355 pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
359 ULONGLONG n = ft.dwHighDateTime;
361 /* Generate a file name to attempt to create.
362 * This algorithm will create what will appear
363 * to be random and unrelated directory names
364 * of up to 9 characters in length.
367 n += ft.dwLowDateTime;
368 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
370 for (k = 0; k < 8; ++k)
374 /* Dividing by a prime greater than 36 helps
375 * with the appearance of randomness
380 pwchDir[k] = '0' + r;
382 pwchDir[k] = 'A' + (r - 10);
385 if (CreateDirectoryW(wszDirPath, 0))
387 /* The following is OK because we generated an
388 * 8 character directory name made from characters
389 * [A-Z0-9], which are equivalent for all code
390 * pages and for UTF-16
392 for (k = 0; k < 8; ++k)
393 pHeader->directory_data[i].filename[k] = pwchDir[k];
398 /* Give up. The most likely cause of this
399 * is a full disk, but whatever the cause
400 * is, it should be more than apparent that
403 dwError = GetLastError();
409 UnmapViewOfFile(pHeader);
413 dwError = GetLastError();
415 CloseHandle(hMapping);
419 dwError = GetLastError();
426 DeleteFileW(wszFilePath);
427 ReleaseMutex(pContainer->hMutex);
433 ReleaseMutex(pContainer->hMutex);
435 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
436 URLCache_PathToObjectName(wszFilePath, '_');
437 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
438 if (!pContainer->hMapping)
439 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
441 if (!pContainer->hMapping)
443 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
444 return GetLastError();
447 return ERROR_SUCCESS;
450 /***********************************************************************
451 * URLCacheContainer_CloseIndex (Internal)
459 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
461 CloseHandle(pContainer->hMapping);
462 pContainer->hMapping = NULL;
465 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
467 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
468 int path_len = strlenW(path);
469 int cache_prefix_len = strlenW(cache_prefix);
476 pContainer->hMapping = NULL;
477 pContainer->file_size = 0;
479 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
480 if (!pContainer->path)
482 HeapFree(GetProcessHeap(), 0, pContainer);
486 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
488 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
489 if (!pContainer->cache_prefix)
491 HeapFree(GetProcessHeap(), 0, pContainer->path);
492 HeapFree(GetProcessHeap(), 0, pContainer);
496 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
498 CharLowerW(mutex_name);
499 URLCache_PathToObjectName(mutex_name, '!');
501 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
503 ERR("couldn't create mutex (error is %d)\n", GetLastError());
504 HeapFree(GetProcessHeap(), 0, pContainer->path);
505 HeapFree(GetProcessHeap(), 0, pContainer);
509 list_add_head(&UrlContainers, &pContainer->entry);
514 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
516 list_remove(&pContainer->entry);
518 URLCacheContainer_CloseIndex(pContainer);
519 CloseHandle(pContainer->hMutex);
520 HeapFree(GetProcessHeap(), 0, pContainer->path);
521 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
522 HeapFree(GetProcessHeap(), 0, pContainer);
525 void URLCacheContainers_CreateDefaults(void)
527 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
528 static const WCHAR UrlPrefix[] = {0};
529 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
530 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
531 static const WCHAR CookieSuffix[] = {0};
532 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
535 int nFolder; /* CSIDL_* constant */
536 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
537 const WCHAR * cache_prefix; /* prefix used to reference the container */
538 } DefaultContainerData[] =
540 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
541 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
542 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
546 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
548 WCHAR wszCachePath[MAX_PATH];
549 WCHAR wszMutexName[MAX_PATH];
550 int path_len, suffix_len;
552 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
554 ERR("Couldn't get path for default container %u\n", i);
557 path_len = strlenW(wszCachePath);
558 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
560 if (path_len + suffix_len + 2 > MAX_PATH)
562 ERR("Path too long\n");
566 wszCachePath[path_len] = '\\';
567 wszCachePath[path_len+1] = 0;
569 strcpyW(wszMutexName, wszCachePath);
573 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
574 wszCachePath[path_len + suffix_len + 1] = '\\';
575 wszCachePath[path_len + suffix_len + 2] = '\0';
578 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
582 void URLCacheContainers_DeleteAll(void)
584 while(!list_empty(&UrlContainers))
585 URLCacheContainer_DeleteContainer(
586 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
590 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
592 URLCACHECONTAINER * pContainer;
594 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
596 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
598 int prefix_len = strlenW(pContainer->cache_prefix);
599 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
601 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
602 *ppContainer = pContainer;
603 return ERROR_SUCCESS;
606 ERR("no container found\n");
607 return ERROR_FILE_NOT_FOUND;
610 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
614 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
615 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
617 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
618 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
619 HeapFree(GetProcessHeap(), 0, lpwszUrl);
622 return GetLastError();
625 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
628 URLCACHECONTAINER * pContainer;
630 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
632 /* non-NULL search pattern only returns one container ever */
633 if (lpwszSearchPattern && dwIndex > 0)
636 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
638 if (lpwszSearchPattern)
640 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
642 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
643 *ppContainer = pContainer;
651 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
652 *ppContainer = pContainer;
661 /***********************************************************************
662 * URLCacheContainer_LockIndex (Internal)
664 * Locks the index for system-wide exclusive access.
667 * Cache file header if successful
668 * NULL if failed and calls SetLastError.
670 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
674 URLCACHE_HEADER * pHeader;
678 WaitForSingleObject(pContainer->hMutex, INFINITE);
680 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
684 ReleaseMutex(pContainer->hMutex);
685 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
688 pHeader = (URLCACHE_HEADER *)pIndexData;
690 /* file has grown - we need to remap to prevent us getting
691 * access violations when we try and access beyond the end
692 * of the memory mapped file */
693 if (pHeader->dwFileSize != pContainer->file_size)
695 UnmapViewOfFile( pHeader );
696 URLCacheContainer_CloseIndex(pContainer);
697 error = URLCacheContainer_OpenIndex(pContainer);
698 if (error != ERROR_SUCCESS)
700 ReleaseMutex(pContainer->hMutex);
704 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
708 ReleaseMutex(pContainer->hMutex);
709 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
712 pHeader = (URLCACHE_HEADER *)pIndexData;
715 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
717 for (index = 0; index < pHeader->DirectoryCount; index++)
719 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
725 /***********************************************************************
726 * URLCacheContainer_UnlockIndex (Internal)
729 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
732 ReleaseMutex(pContainer->hMutex);
733 return UnmapViewOfFile(pHeader);
738 #define CHAR_BIT (8 * sizeof(CHAR))
741 /***********************************************************************
742 * URLCache_Allocation_BlockIsFree (Internal)
744 * Is the specified block number free?
751 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
753 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
754 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
757 /***********************************************************************
758 * URLCache_Allocation_BlockFree (Internal)
760 * Marks the specified block as free
766 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
768 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
769 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
772 /***********************************************************************
773 * URLCache_Allocation_BlockAllocate (Internal)
775 * Marks the specified block as allocated
781 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
783 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
784 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
787 /***********************************************************************
788 * URLCache_FindFirstFreeEntry (Internal)
790 * Finds and allocates the first block of free space big enough and
791 * sets ppEntry to point to it.
794 * TRUE if it had enough space
795 * FALSE if it couldn't find enough space
798 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
800 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
803 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
805 for (dwFreeCounter = 0;
806 dwFreeCounter < dwBlocksNeeded &&
807 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
808 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
810 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
812 if (dwFreeCounter == dwBlocksNeeded)
815 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
816 for (index = 0; index < dwBlocksNeeded; index++)
817 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
818 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
819 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
823 FIXME("Grow file\n");
827 /***********************************************************************
828 * URLCache_DeleteEntry (Internal)
830 * Deletes the specified entry and frees the space allocated to it
833 * TRUE if it succeeded
837 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
841 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
843 /* update allocation table */
844 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
845 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
846 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
848 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
852 /***********************************************************************
853 * URLCache_LocalFileNameToPathW (Internal)
855 * Copies the full path to the specified buffer given the local file
856 * name and the index of the directory it is in. Always sets value in
857 * lpBufferSize to the required buffer size (in bytes).
860 * TRUE if the buffer was big enough
861 * FALSE if the buffer was too small
864 static BOOL URLCache_LocalFileNameToPathW(
865 const URLCACHECONTAINER * pContainer,
866 LPCURLCACHE_HEADER pHeader,
867 LPCSTR szLocalFileName,
873 int path_len = strlenW(pContainer->path);
874 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
875 if (Directory >= pHeader->DirectoryCount)
881 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
882 if (nRequired < *lpBufferSize)
886 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
887 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
888 wszPath[dir_len + path_len] = '\\';
889 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
890 *lpBufferSize = nRequired;
893 *lpBufferSize = nRequired;
897 /***********************************************************************
898 * URLCache_LocalFileNameToPathA (Internal)
900 * Copies the full path to the specified buffer given the local file
901 * name and the index of the directory it is in. Always sets value in
902 * lpBufferSize to the required buffer size.
905 * TRUE if the buffer was big enough
906 * FALSE if the buffer was too small
909 static BOOL URLCache_LocalFileNameToPathA(
910 const URLCACHECONTAINER * pContainer,
911 LPCURLCACHE_HEADER pHeader,
912 LPCSTR szLocalFileName,
918 int path_len, file_name_len, dir_len;
920 if (Directory >= pHeader->DirectoryCount)
926 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
927 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
928 dir_len = DIR_LENGTH;
930 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
931 if (nRequired < *lpBufferSize)
933 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
934 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
935 szPath[path_len + dir_len] = '\\';
936 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
937 *lpBufferSize = nRequired;
940 *lpBufferSize = nRequired;
944 /***********************************************************************
945 * URLCache_CopyEntry (Internal)
947 * Copies an entry from the cache index file to the Win32 structure
950 * ERROR_SUCCESS if the buffer was big enough
951 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
954 static DWORD URLCache_CopyEntry(
955 URLCACHECONTAINER * pContainer,
956 LPCURLCACHE_HEADER pHeader,
957 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
958 LPDWORD lpdwBufferSize,
959 const URL_CACHEFILE_ENTRY * pUrlEntry,
963 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
965 if (*lpdwBufferSize >= dwRequiredSize)
967 lpCacheEntryInfo->lpHeaderInfo = NULL;
968 lpCacheEntryInfo->lpszFileExtension = NULL;
969 lpCacheEntryInfo->lpszLocalFileName = NULL;
970 lpCacheEntryInfo->lpszSourceUrlName = NULL;
971 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
972 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
973 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
974 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
975 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
976 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
977 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
978 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
979 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
980 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
981 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
982 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
983 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
984 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
987 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
988 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
989 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
991 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
993 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
994 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
996 /* FIXME: is source url optional? */
997 if (*lpdwBufferSize >= dwRequiredSize)
999 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1001 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1003 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1005 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1008 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1009 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1010 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1012 if (pUrlEntry->dwOffsetLocalName)
1014 LONG nLocalFilePathSize;
1015 LPSTR lpszLocalFileName;
1016 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1017 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1018 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1019 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1021 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1023 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1025 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1026 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1027 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1029 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1031 if (*lpdwBufferSize >= dwRequiredSize)
1033 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1034 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1035 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1037 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1038 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1039 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1041 if (pUrlEntry->dwOffsetFileExtension)
1046 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1048 lenExtension = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1049 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1051 if (*lpdwBufferSize >= dwRequiredSize)
1053 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1055 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1057 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1060 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1061 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1062 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1065 if (dwRequiredSize > *lpdwBufferSize)
1067 *lpdwBufferSize = dwRequiredSize;
1068 return ERROR_INSUFFICIENT_BUFFER;
1070 *lpdwBufferSize = dwRequiredSize;
1071 return ERROR_SUCCESS;
1075 /***********************************************************************
1076 * URLCache_SetEntryInfo (Internal)
1078 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1079 * according to the flags set by dwFieldControl.
1082 * ERROR_SUCCESS if the buffer was big enough
1083 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1086 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1088 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1089 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1090 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1091 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1092 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1093 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1094 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1095 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1096 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1097 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1098 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1099 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1100 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1101 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1102 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1103 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1105 return ERROR_SUCCESS;
1108 /***********************************************************************
1109 * URLCache_HashKey (Internal)
1111 * Returns the hash key for a given string
1114 * hash key for the string
1117 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1119 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1120 * but the algorithm and result are not the same!
1122 static const unsigned char lookupTable[256] =
1124 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1125 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1126 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1127 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1128 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1129 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1130 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1131 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1132 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1133 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1134 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1135 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1136 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1137 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1138 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1139 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1140 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1141 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1142 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1143 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1144 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1145 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1146 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1147 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1148 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1149 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1150 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1151 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1152 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1153 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1154 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1155 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1160 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1161 key[i] = lookupTable[i];
1163 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1165 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1166 key[i] = lookupTable[*lpszKey ^ key[i]];
1169 return *(DWORD *)key;
1172 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1174 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1177 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1179 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1180 return ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) &&
1181 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1184 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1186 /* structure of hash table:
1187 * 448 entries divided into 64 blocks
1188 * each block therefore contains a chain of 7 key/offset pairs
1189 * how position in table is calculated:
1190 * 1. the url is hashed in helper function
1191 * 2. the key % 64 * 8 is the offset
1192 * 3. the key in the hash table is the hash key aligned to 64
1195 * there can be multiple hash tables in the file and the offset to
1196 * the next one is stored in the header of the hash table
1198 DWORD key = URLCache_HashKey(lpszUrl);
1199 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1200 HASH_CACHEFILE_ENTRY * pHashEntry;
1201 DWORD dwHashTableNumber = 0;
1203 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1205 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1206 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1207 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1210 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1212 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1215 /* make sure that it is in fact a hash entry */
1216 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1218 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1222 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1224 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1225 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1227 /* FIXME: we should make sure that this is the right element
1228 * before returning and claiming that it is. We can do this
1229 * by doing a simple compare between the URL we were given
1230 * and the URL stored in the entry. However, this assumes
1231 * we know the format of all the entries stored in the
1233 *ppHashEntry = pHashElement;
1241 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1247 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1248 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1251 SetLastError(ERROR_OUTOFMEMORY);
1254 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, urlA, url_len, NULL, NULL);
1255 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1256 HeapFree(GetProcessHeap(), 0, urlA);
1260 /***********************************************************************
1261 * URLCache_HashEntrySetUse (Internal)
1263 * Searches all the hash tables in the index for the given URL and
1264 * sets the use count (stored or'ed with key)
1267 * TRUE if the entry was found
1268 * FALSE if the entry could not be found
1271 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1273 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1277 /***********************************************************************
1278 * URLCache_DeleteEntryFromHash (Internal)
1280 * Searches all the hash tables in the index for the given URL and
1281 * then if found deletes the entry.
1284 * TRUE if the entry was found
1285 * FALSE if the entry could not be found
1288 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1290 pHashEntry->dwHashKey = HASHTABLE_FREE;
1291 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1295 /***********************************************************************
1296 * URLCache_AddEntryToHash (Internal)
1298 * Searches all the hash tables for a free slot based on the offset
1299 * generated from the hash key. If a free slot is found, the offset and
1300 * key are entered into the hash table.
1303 * ERROR_SUCCESS if the entry was added
1304 * Any other Win32 error code if the entry could not be added
1307 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1309 /* see URLCache_FindEntryInHash for structure of hash tables */
1311 DWORD key = URLCache_HashKey(lpszUrl);
1312 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1313 HASH_CACHEFILE_ENTRY * pHashEntry;
1314 DWORD dwHashTableNumber = 0;
1317 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1319 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1320 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1321 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1324 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1326 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1329 /* make sure that it is in fact a hash entry */
1330 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1332 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1336 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1338 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1339 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1341 pHashElement->dwHashKey = key;
1342 pHashElement->dwOffsetEntry = dwOffsetEntry;
1343 return ERROR_SUCCESS;
1347 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1348 if (error != ERROR_SUCCESS)
1351 pHashEntry->HashTable[offset].dwHashKey = key;
1352 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1353 return ERROR_SUCCESS;
1356 /***********************************************************************
1357 * URLCache_CreateHashTable (Internal)
1359 * Creates a new hash table in free space and adds it to the chain of existing
1363 * ERROR_SUCCESS if the hash table was created
1364 * ERROR_DISK_FULL if the hash table could not be created
1367 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1372 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1374 FIXME("no free space for hash table\n");
1375 return ERROR_DISK_FULL;
1378 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1381 pPrevHash->dwAddressNext = dwOffset;
1383 pHeader->dwOffsetFirstHashTable = dwOffset;
1384 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1385 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1386 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1387 for (i = 0; i < HASHTABLE_SIZE; i++)
1389 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1390 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1392 return ERROR_SUCCESS;
1395 /***********************************************************************
1396 * URLCache_EnumHashTables (Internal)
1398 * Enumerates the hash tables in a container.
1401 * TRUE if an entry was found
1402 * FALSE if there are no more tables to enumerate.
1405 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1407 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1408 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1409 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1411 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1412 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1414 /* make sure that it is in fact a hash entry */
1415 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1417 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1418 (*pdwHashTableNumber)++;
1422 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1428 /***********************************************************************
1429 * URLCache_EnumHashTableEntries (Internal)
1431 * Enumerates entries in a hash table and returns the next non-free entry.
1434 * TRUE if an entry was found
1435 * FALSE if the hash table is empty or there are no more entries to
1439 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1440 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1442 for (; *index < HASHTABLE_SIZE ; (*index)++)
1444 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1447 *ppHashEntry = &pHashEntry->HashTable[*index];
1448 TRACE("entry found %d\n", *index);
1451 TRACE("no more entries (%d)\n", *index);
1455 /***********************************************************************
1456 * GetUrlCacheEntryInfoExA (WININET.@)
1459 BOOL WINAPI GetUrlCacheEntryInfoExA(
1461 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1462 LPDWORD lpdwCacheEntryInfoBufSize,
1464 LPDWORD lpdwReserved,
1468 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1469 debugstr_a(lpszUrl),
1471 lpdwCacheEntryInfoBufSize,
1477 if ((lpszReserved != NULL) ||
1478 (lpdwReserved != NULL) ||
1479 (lpReserved != NULL))
1481 ERR("Reserved value was not 0\n");
1482 SetLastError(ERROR_INVALID_PARAMETER);
1486 FIXME("Undocumented flag(s): %x\n", dwFlags);
1487 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1490 /***********************************************************************
1491 * GetUrlCacheEntryInfoA (WININET.@)
1494 BOOL WINAPI GetUrlCacheEntryInfoA(
1495 IN LPCSTR lpszUrlName,
1496 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1497 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1500 LPURLCACHE_HEADER pHeader;
1501 struct _HASH_ENTRY * pHashEntry;
1502 const CACHEFILE_ENTRY * pEntry;
1503 const URL_CACHEFILE_ENTRY * pUrlEntry;
1504 URLCACHECONTAINER * pContainer;
1507 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1509 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1510 if (error != ERROR_SUCCESS)
1512 SetLastError(error);
1516 error = URLCacheContainer_OpenIndex(pContainer);
1517 if (error != ERROR_SUCCESS)
1519 SetLastError(error);
1523 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1526 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1528 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1529 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1530 SetLastError(ERROR_FILE_NOT_FOUND);
1534 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1535 if (pEntry->dwSignature != URL_SIGNATURE)
1537 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1538 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1539 SetLastError(ERROR_FILE_NOT_FOUND);
1543 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1544 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1545 if (pUrlEntry->dwOffsetHeaderInfo)
1546 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1548 if (lpdwCacheEntryInfoBufferSize)
1550 if (!lpCacheEntryInfo)
1551 *lpdwCacheEntryInfoBufferSize = 0;
1553 error = URLCache_CopyEntry(
1557 lpdwCacheEntryInfoBufferSize,
1560 if (error != ERROR_SUCCESS)
1562 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1563 SetLastError(error);
1566 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1569 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1574 /***********************************************************************
1575 * GetUrlCacheEntryInfoW (WININET.@)
1578 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1579 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1580 LPDWORD lpdwCacheEntryInfoBufferSize)
1582 LPURLCACHE_HEADER pHeader;
1583 struct _HASH_ENTRY * pHashEntry;
1584 const CACHEFILE_ENTRY * pEntry;
1585 const URL_CACHEFILE_ENTRY * pUrlEntry;
1586 URLCACHECONTAINER * pContainer;
1589 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1591 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1592 if (error != ERROR_SUCCESS)
1594 SetLastError(error);
1598 error = URLCacheContainer_OpenIndex(pContainer);
1599 if (error != ERROR_SUCCESS)
1601 SetLastError(error);
1605 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1608 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1610 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1611 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1612 SetLastError(ERROR_FILE_NOT_FOUND);
1616 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1617 if (pEntry->dwSignature != URL_SIGNATURE)
1619 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1620 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1621 SetLastError(ERROR_FILE_NOT_FOUND);
1625 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1626 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1627 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1629 if (lpdwCacheEntryInfoBufferSize)
1631 if (!lpCacheEntryInfo)
1632 *lpdwCacheEntryInfoBufferSize = 0;
1634 error = URLCache_CopyEntry(
1637 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1638 lpdwCacheEntryInfoBufferSize,
1640 TRUE /* UNICODE */);
1641 if (error != ERROR_SUCCESS)
1643 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1644 SetLastError(error);
1647 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1650 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1655 /***********************************************************************
1656 * GetUrlCacheEntryInfoExW (WININET.@)
1659 BOOL WINAPI GetUrlCacheEntryInfoExW(
1661 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1662 LPDWORD lpdwCacheEntryInfoBufSize,
1663 LPWSTR lpszReserved,
1664 LPDWORD lpdwReserved,
1668 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1669 debugstr_w(lpszUrl),
1671 lpdwCacheEntryInfoBufSize,
1677 if ((lpszReserved != NULL) ||
1678 (lpdwReserved != NULL) ||
1679 (lpReserved != NULL))
1681 ERR("Reserved value was not 0\n");
1682 SetLastError(ERROR_INVALID_PARAMETER);
1686 FIXME("Undocumented flag(s): %x\n", dwFlags);
1687 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1690 /***********************************************************************
1691 * SetUrlCacheEntryInfoA (WININET.@)
1693 BOOL WINAPI SetUrlCacheEntryInfoA(
1695 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1696 DWORD dwFieldControl)
1698 LPURLCACHE_HEADER pHeader;
1699 struct _HASH_ENTRY * pHashEntry;
1700 CACHEFILE_ENTRY * pEntry;
1701 URLCACHECONTAINER * pContainer;
1704 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1706 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1707 if (error != ERROR_SUCCESS)
1709 SetLastError(error);
1713 error = URLCacheContainer_OpenIndex(pContainer);
1714 if (error != ERROR_SUCCESS)
1716 SetLastError(error);
1720 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1723 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1725 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1726 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1727 SetLastError(ERROR_FILE_NOT_FOUND);
1731 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1732 if (pEntry->dwSignature != URL_SIGNATURE)
1734 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1735 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1736 SetLastError(ERROR_FILE_NOT_FOUND);
1740 URLCache_SetEntryInfo(
1741 (URL_CACHEFILE_ENTRY *)pEntry,
1742 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1745 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1750 /***********************************************************************
1751 * SetUrlCacheEntryInfoW (WININET.@)
1753 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1755 LPURLCACHE_HEADER pHeader;
1756 struct _HASH_ENTRY * pHashEntry;
1757 CACHEFILE_ENTRY * pEntry;
1758 URLCACHECONTAINER * pContainer;
1761 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1763 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1764 if (error != ERROR_SUCCESS)
1766 SetLastError(error);
1770 error = URLCacheContainer_OpenIndex(pContainer);
1771 if (error != ERROR_SUCCESS)
1773 SetLastError(error);
1777 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1780 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1782 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1783 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1784 SetLastError(ERROR_FILE_NOT_FOUND);
1788 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1789 if (pEntry->dwSignature != URL_SIGNATURE)
1791 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1792 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1793 SetLastError(ERROR_FILE_NOT_FOUND);
1797 URLCache_SetEntryInfo(
1798 (URL_CACHEFILE_ENTRY *)pEntry,
1802 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1807 /***********************************************************************
1808 * RetrieveUrlCacheEntryFileA (WININET.@)
1811 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1812 IN LPCSTR lpszUrlName,
1813 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1814 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1818 LPURLCACHE_HEADER pHeader;
1819 struct _HASH_ENTRY * pHashEntry;
1820 CACHEFILE_ENTRY * pEntry;
1821 URL_CACHEFILE_ENTRY * pUrlEntry;
1822 URLCACHECONTAINER * pContainer;
1825 TRACE("(%s, %p, %p, 0x%08x)\n",
1826 debugstr_a(lpszUrlName),
1828 lpdwCacheEntryInfoBufferSize,
1831 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1832 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1834 SetLastError(ERROR_INVALID_PARAMETER);
1838 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1839 if (error != ERROR_SUCCESS)
1841 SetLastError(error);
1845 error = URLCacheContainer_OpenIndex(pContainer);
1846 if (error != ERROR_SUCCESS)
1848 SetLastError(error);
1852 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1855 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1857 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1858 TRACE("entry %s not found!\n", lpszUrlName);
1859 SetLastError(ERROR_FILE_NOT_FOUND);
1863 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1864 if (pEntry->dwSignature != URL_SIGNATURE)
1866 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1867 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1868 SetLastError(ERROR_FILE_NOT_FOUND);
1872 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1873 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1874 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1876 pUrlEntry->dwHitRate++;
1877 pUrlEntry->dwUseCount++;
1878 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1880 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
1881 lpdwCacheEntryInfoBufferSize, pUrlEntry,
1883 if (error != ERROR_SUCCESS)
1885 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1886 SetLastError(error);
1889 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1891 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1896 /***********************************************************************
1897 * RetrieveUrlCacheEntryFileW (WININET.@)
1900 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1901 IN LPCWSTR lpszUrlName,
1902 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1903 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1907 LPURLCACHE_HEADER pHeader;
1908 struct _HASH_ENTRY * pHashEntry;
1909 CACHEFILE_ENTRY * pEntry;
1910 URL_CACHEFILE_ENTRY * pUrlEntry;
1911 URLCACHECONTAINER * pContainer;
1914 TRACE("(%s, %p, %p, 0x%08x)\n",
1915 debugstr_w(lpszUrlName),
1917 lpdwCacheEntryInfoBufferSize,
1920 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1921 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1923 SetLastError(ERROR_INVALID_PARAMETER);
1927 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
1928 if (error != ERROR_SUCCESS)
1930 SetLastError(error);
1934 error = URLCacheContainer_OpenIndex(pContainer);
1935 if (error != ERROR_SUCCESS)
1937 SetLastError(error);
1941 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1944 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1946 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1947 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1948 SetLastError(ERROR_FILE_NOT_FOUND);
1952 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1953 if (pEntry->dwSignature != URL_SIGNATURE)
1955 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1956 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1957 SetLastError(ERROR_FILE_NOT_FOUND);
1961 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1962 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1963 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1965 pUrlEntry->dwHitRate++;
1966 pUrlEntry->dwUseCount++;
1967 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1969 error = URLCache_CopyEntry(
1972 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1973 lpdwCacheEntryInfoBufferSize,
1975 TRUE /* UNICODE */);
1976 if (error != ERROR_SUCCESS)
1978 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1979 SetLastError(error);
1982 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1984 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1989 /***********************************************************************
1990 * UnlockUrlCacheEntryFileA (WININET.@)
1993 BOOL WINAPI UnlockUrlCacheEntryFileA(
1994 IN LPCSTR lpszUrlName,
1998 LPURLCACHE_HEADER pHeader;
1999 struct _HASH_ENTRY * pHashEntry;
2000 CACHEFILE_ENTRY * pEntry;
2001 URL_CACHEFILE_ENTRY * pUrlEntry;
2002 URLCACHECONTAINER * pContainer;
2005 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2009 ERR("dwReserved != 0\n");
2010 SetLastError(ERROR_INVALID_PARAMETER);
2014 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2015 if (error != ERROR_SUCCESS)
2017 SetLastError(error);
2021 error = URLCacheContainer_OpenIndex(pContainer);
2022 if (error != ERROR_SUCCESS)
2024 SetLastError(error);
2028 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2031 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2033 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2034 TRACE("entry %s not found!\n", lpszUrlName);
2035 SetLastError(ERROR_FILE_NOT_FOUND);
2039 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2040 if (pEntry->dwSignature != URL_SIGNATURE)
2042 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2043 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2044 SetLastError(ERROR_FILE_NOT_FOUND);
2048 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2050 if (pUrlEntry->dwUseCount == 0)
2052 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2055 pUrlEntry->dwUseCount--;
2056 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2058 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2063 /***********************************************************************
2064 * UnlockUrlCacheEntryFileW (WININET.@)
2067 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2069 LPURLCACHE_HEADER pHeader;
2070 struct _HASH_ENTRY * pHashEntry;
2071 CACHEFILE_ENTRY * pEntry;
2072 URL_CACHEFILE_ENTRY * pUrlEntry;
2073 URLCACHECONTAINER * pContainer;
2076 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2080 ERR("dwReserved != 0\n");
2081 SetLastError(ERROR_INVALID_PARAMETER);
2085 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2086 if (error != ERROR_SUCCESS)
2088 SetLastError(error);
2092 error = URLCacheContainer_OpenIndex(pContainer);
2093 if (error != ERROR_SUCCESS)
2095 SetLastError(error);
2099 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2102 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2104 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2105 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2106 SetLastError(ERROR_FILE_NOT_FOUND);
2110 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2111 if (pEntry->dwSignature != URL_SIGNATURE)
2113 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2114 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2115 SetLastError(ERROR_FILE_NOT_FOUND);
2119 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2121 if (pUrlEntry->dwUseCount == 0)
2123 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2126 pUrlEntry->dwUseCount--;
2127 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2129 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2134 /***********************************************************************
2135 * CreateUrlCacheEntryA (WININET.@)
2138 BOOL WINAPI CreateUrlCacheEntryA(
2139 IN LPCSTR lpszUrlName,
2140 IN DWORD dwExpectedFileSize,
2141 IN LPCSTR lpszFileExtension,
2142 OUT LPSTR lpszFileName,
2148 WCHAR *file_extension;
2149 WCHAR file_name[MAX_PATH];
2150 BOOL bSuccess = FALSE;
2153 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2154 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2156 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2157 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2158 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2160 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2161 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2163 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2169 dwError = GetLastError();
2174 dwError = GetLastError();
2176 HeapFree(GetProcessHeap(), 0, file_extension);
2180 dwError = GetLastError();
2182 HeapFree(GetProcessHeap(), 0, url_name);
2184 SetLastError(dwError);
2188 /***********************************************************************
2189 * CreateUrlCacheEntryW (WININET.@)
2192 BOOL WINAPI CreateUrlCacheEntryW(
2193 IN LPCWSTR lpszUrlName,
2194 IN DWORD dwExpectedFileSize,
2195 IN LPCWSTR lpszFileExtension,
2196 OUT LPWSTR lpszFileName,
2200 URLCACHECONTAINER * pContainer;
2201 LPURLCACHE_HEADER pHeader;
2202 CHAR szFile[MAX_PATH];
2203 WCHAR szExtension[MAX_PATH];
2204 LPCWSTR lpszUrlPart;
2206 LPCWSTR lpszFileNameExtension;
2207 LPWSTR lpszFileNameNoPath;
2209 int countnoextension;
2212 BOOL bFound = FALSE;
2215 static const WCHAR szWWW[] = {'w','w','w',0};
2217 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2218 debugstr_w(lpszUrlName),
2220 debugstr_w(lpszFileExtension),
2225 FIXME("dwReserved 0x%08x\n", dwReserved);
2227 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2229 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2232 for (lpszUrlPart = lpszUrlEnd;
2233 (lpszUrlPart >= lpszUrlName);
2236 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2242 else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2244 lpszUrlEnd = lpszUrlPart;
2247 if (!lstrcmpW(lpszUrlPart, szWWW))
2249 lpszUrlPart += lstrlenW(szWWW);
2252 count = lpszUrlEnd - lpszUrlPart;
2254 if (bFound && (count < MAX_PATH))
2256 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2260 while(len && szFile[--len] == '/') szFile[len] = '\0';
2262 /* FIXME: get rid of illegal characters like \, / and : */
2266 FIXME("need to generate a random filename\n");
2269 TRACE("File name: %s\n", debugstr_a(szFile));
2271 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2272 if (error != ERROR_SUCCESS)
2274 SetLastError(error);
2278 error = URLCacheContainer_OpenIndex(pContainer);
2279 if (error != ERROR_SUCCESS)
2281 SetLastError(error);
2285 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2288 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2290 lBufferSize = MAX_PATH * sizeof(WCHAR);
2291 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2293 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2295 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2296 lpszFileNameNoPath >= lpszFileName;
2297 --lpszFileNameNoPath)
2299 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2303 countnoextension = lstrlenW(lpszFileNameNoPath);
2304 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2305 if (lpszFileNameExtension)
2306 countnoextension -= lstrlenW(lpszFileNameExtension);
2307 *szExtension = '\0';
2309 if (lpszFileExtension)
2311 szExtension[0] = '.';
2312 lstrcpyW(szExtension+1, lpszFileExtension);
2315 for (i = 0; i < 255; i++)
2317 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2321 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2322 for (p = lpszFileNameNoPath + 1; *p; p++)
2328 case '/': case '\\':
2335 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2337 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2338 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2339 if (hFile != INVALID_HANDLE_VALUE)
2350 /***********************************************************************
2351 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2353 * The bug we are compensating for is that some drongo at Microsoft
2354 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2355 * As a consequence, CommitUrlCacheEntryA has been effectively
2356 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2357 * is still defined as LPCWSTR. The result (other than madness) is
2358 * that we always need to store lpHeaderInfo in CP_ACP rather than
2359 * in UTF16, and we need to avoid converting lpHeaderInfo in
2360 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2361 * result will lose data for arbitrary binary data.
2364 static BOOL CommitUrlCacheEntryInternal(
2365 IN LPCWSTR lpszUrlName,
2366 IN LPCWSTR lpszLocalFileName,
2367 IN FILETIME ExpireTime,
2368 IN FILETIME LastModifiedTime,
2369 IN DWORD CacheEntryType,
2370 IN LPBYTE lpHeaderInfo,
2371 IN DWORD dwHeaderSize,
2372 IN LPCWSTR lpszFileExtension,
2373 IN LPCWSTR lpszOriginalUrl
2376 URLCACHECONTAINER * pContainer;
2377 LPURLCACHE_HEADER pHeader;
2378 struct _HASH_ENTRY * pHashEntry;
2379 CACHEFILE_ENTRY * pEntry;
2380 URL_CACHEFILE_ENTRY * pUrlEntry;
2381 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2382 DWORD dwOffsetLocalFileName = 0;
2383 DWORD dwOffsetHeader = 0;
2384 DWORD dwOffsetFileExtension = 0;
2385 DWORD dwFileSizeLow = 0;
2386 DWORD dwFileSizeHigh = 0;
2387 BYTE cDirectory = 0;
2389 char achFile[MAX_PATH];
2390 LPSTR lpszUrlNameA = NULL;
2391 LPSTR lpszFileExtensionA = NULL;
2392 char *pchLocalFileName = 0;
2395 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2396 debugstr_w(lpszUrlName),
2397 debugstr_w(lpszLocalFileName),
2401 debugstr_w(lpszFileExtension),
2402 debugstr_w(lpszOriginalUrl));
2404 if (lpszOriginalUrl)
2405 WARN(": lpszOriginalUrl ignored\n");
2407 if (lpszLocalFileName)
2411 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2412 if (hFile == INVALID_HANDLE_VALUE)
2414 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2419 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2420 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2422 ERR("couldn't get file size (error is %d)\n", GetLastError());
2430 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2431 if (error != ERROR_SUCCESS)
2433 SetLastError(error);
2437 error = URLCacheContainer_OpenIndex(pContainer);
2438 if (error != ERROR_SUCCESS)
2440 SetLastError(error);
2444 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2447 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2448 lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2451 error = GetLastError();
2454 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2456 if (lpszFileExtension)
2458 len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2459 lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2460 if (!lpszFileExtensionA)
2462 error = GetLastError();
2465 WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2468 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2470 FIXME("entry already in cache - don't know what to do!\n");
2472 * SetLastError(ERROR_FILE_NOT_FOUND);
2478 if (lpszLocalFileName)
2480 BOOL bFound = FALSE;
2482 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2484 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2485 error = ERROR_INVALID_PARAMETER;
2489 /* skip container path prefix */
2490 lpszLocalFileName += lstrlenW(pContainer->path);
2492 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2493 pchLocalFileName = achFile;
2495 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2497 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2506 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2507 error = ERROR_INVALID_PARAMETER;
2511 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2514 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2515 if (lpszLocalFileName)
2517 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2518 dwOffsetLocalFileName = dwBytesNeeded;
2519 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2523 dwOffsetHeader = dwBytesNeeded;
2524 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2526 if (lpszFileExtensionA)
2528 dwOffsetFileExtension = dwBytesNeeded;
2529 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2532 /* round up to next block */
2533 if (dwBytesNeeded % BLOCKSIZE)
2535 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2536 dwBytesNeeded += BLOCKSIZE;
2539 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2541 ERR("no free entries\n");
2542 error = ERROR_DISK_FULL;
2546 /* FindFirstFreeEntry fills in blocks used */
2547 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2548 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2549 pUrlEntry->CacheDir = cDirectory;
2550 pUrlEntry->CacheEntryType = CacheEntryType;
2551 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2552 pUrlEntry->dwExemptDelta = 0;
2553 pUrlEntry->dwHitRate = 0;
2554 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2555 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2556 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2557 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2558 pUrlEntry->dwSizeHigh = 0;
2559 pUrlEntry->dwSizeLow = dwFileSizeLow;
2560 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2561 pUrlEntry->dwUseCount = 0;
2562 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2563 pUrlEntry->LastModifiedTime = LastModifiedTime;
2564 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2565 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2566 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2567 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2570 pUrlEntry->dwUnknown1 = 0;
2571 pUrlEntry->dwUnknown2 = 0;
2572 pUrlEntry->dwUnknown3 = 0x60;
2573 pUrlEntry->Unknown4 = 0;
2574 pUrlEntry->wUnknown5 = 0x1010;
2575 pUrlEntry->dwUnknown7 = 0;
2576 pUrlEntry->dwUnknown8 = 0;
2579 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2580 if (dwOffsetLocalFileName)
2581 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2583 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2584 if (dwOffsetFileExtension)
2585 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2587 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2588 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2589 if (error != ERROR_SUCCESS)
2590 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2593 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2594 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2595 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2597 if (error == ERROR_SUCCESS)
2601 SetLastError(error);
2606 /***********************************************************************
2607 * CommitUrlCacheEntryA (WININET.@)
2610 BOOL WINAPI CommitUrlCacheEntryA(
2611 IN LPCSTR lpszUrlName,
2612 IN LPCSTR lpszLocalFileName,
2613 IN FILETIME ExpireTime,
2614 IN FILETIME LastModifiedTime,
2615 IN DWORD CacheEntryType,
2616 IN LPBYTE lpHeaderInfo,
2617 IN DWORD dwHeaderSize,
2618 IN LPCSTR lpszFileExtension,
2619 IN LPCSTR lpszOriginalUrl
2623 WCHAR *url_name = NULL;
2624 WCHAR *local_file_name = NULL;
2625 WCHAR *original_url = NULL;
2626 WCHAR *file_extension = NULL;
2627 BOOL bSuccess = FALSE;
2629 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2630 debugstr_a(lpszUrlName),
2631 debugstr_a(lpszLocalFileName),
2635 debugstr_a(lpszFileExtension),
2636 debugstr_a(lpszOriginalUrl));
2638 len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2639 url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2642 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2644 if (lpszLocalFileName)
2646 len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2647 local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2648 if (!local_file_name)
2650 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2652 if (lpszFileExtension)
2654 len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2655 file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2656 if (!file_extension)
2658 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2660 if (lpszOriginalUrl)
2662 len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2663 original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2666 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2669 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2670 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2671 file_extension, original_url);
2674 HeapFree(GetProcessHeap(), 0, original_url);
2675 HeapFree(GetProcessHeap(), 0, file_extension);
2676 HeapFree(GetProcessHeap(), 0, local_file_name);
2677 HeapFree(GetProcessHeap(), 0, url_name);
2682 /***********************************************************************
2683 * CommitUrlCacheEntryW (WININET.@)
2686 BOOL WINAPI CommitUrlCacheEntryW(
2687 IN LPCWSTR lpszUrlName,
2688 IN LPCWSTR lpszLocalFileName,
2689 IN FILETIME ExpireTime,
2690 IN FILETIME LastModifiedTime,
2691 IN DWORD CacheEntryType,
2692 IN LPWSTR lpHeaderInfo,
2693 IN DWORD dwHeaderSize,
2694 IN LPCWSTR lpszFileExtension,
2695 IN LPCWSTR lpszOriginalUrl
2699 BOOL bSuccess = FALSE;
2701 CHAR *header_info = NULL;
2703 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2704 debugstr_w(lpszUrlName),
2705 debugstr_w(lpszLocalFileName),
2709 debugstr_w(lpszFileExtension),
2710 debugstr_w(lpszOriginalUrl));
2712 if (!lpHeaderInfo ||
2713 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2714 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2717 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2718 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2719 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2725 dwError = GetLastError();
2729 HeapFree(GetProcessHeap(), 0, header_info);
2731 SetLastError(dwError);
2737 /***********************************************************************
2738 * ReadUrlCacheEntryStream (WININET.@)
2741 BOOL WINAPI ReadUrlCacheEntryStream(
2742 IN HANDLE hUrlCacheStream,
2743 IN DWORD dwLocation,
2744 IN OUT LPVOID lpBuffer,
2745 IN OUT LPDWORD lpdwLen,
2749 /* Get handle to file from 'stream' */
2750 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2752 if (dwReserved != 0)
2754 ERR("dwReserved != 0\n");
2755 SetLastError(ERROR_INVALID_PARAMETER);
2759 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2761 SetLastError(ERROR_INVALID_HANDLE);
2765 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2767 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2770 /***********************************************************************
2771 * RetrieveUrlCacheEntryStreamA (WININET.@)
2774 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2775 IN LPCSTR lpszUrlName,
2776 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2777 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2778 IN BOOL fRandomRead,
2782 /* NOTE: this is not the same as the way that the native
2783 * version allocates 'stream' handles. I did it this way
2784 * as it is much easier and no applications should depend
2785 * on this behaviour. (Native version appears to allocate
2786 * indices into a table)
2788 STREAM_HANDLE * pStream;
2791 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2792 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2794 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2796 lpdwCacheEntryInfoBufferSize,
2802 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2807 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2809 if (hFile == INVALID_HANDLE_VALUE)
2812 /* allocate handle storage space */
2813 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2817 SetLastError(ERROR_OUTOFMEMORY);
2821 pStream->hFile = hFile;
2822 strcpy(pStream->lpszUrl, lpszUrlName);
2826 /***********************************************************************
2827 * RetrieveUrlCacheEntryStreamW (WININET.@)
2830 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2831 IN LPCWSTR lpszUrlName,
2832 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2833 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2834 IN BOOL fRandomRead,
2838 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2839 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2843 /***********************************************************************
2844 * UnlockUrlCacheEntryStream (WININET.@)
2847 BOOL WINAPI UnlockUrlCacheEntryStream(
2848 IN HANDLE hUrlCacheStream,
2852 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2854 if (dwReserved != 0)
2856 ERR("dwReserved != 0\n");
2857 SetLastError(ERROR_INVALID_PARAMETER);
2861 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2863 SetLastError(ERROR_INVALID_HANDLE);
2867 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2870 /* close file handle */
2871 CloseHandle(pStream->hFile);
2873 /* free allocated space */
2874 HeapFree(GetProcessHeap(), 0, pStream);
2880 /***********************************************************************
2881 * DeleteUrlCacheEntryA (WININET.@)
2884 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2886 URLCACHECONTAINER * pContainer;
2887 LPURLCACHE_HEADER pHeader;
2888 struct _HASH_ENTRY * pHashEntry;
2889 CACHEFILE_ENTRY * pEntry;
2892 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2894 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2895 if (error != ERROR_SUCCESS)
2897 SetLastError(error);
2901 error = URLCacheContainer_OpenIndex(pContainer);
2902 if (error != ERROR_SUCCESS)
2904 SetLastError(error);
2908 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2911 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2913 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2914 TRACE("entry %s not found!\n", lpszUrlName);
2915 SetLastError(ERROR_FILE_NOT_FOUND);
2919 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2920 URLCache_DeleteEntry(pHeader, pEntry);
2922 URLCache_DeleteEntryFromHash(pHashEntry);
2924 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2929 /***********************************************************************
2930 * DeleteUrlCacheEntryW (WININET.@)
2933 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2935 URLCACHECONTAINER * pContainer;
2936 LPURLCACHE_HEADER pHeader;
2937 struct _HASH_ENTRY * pHashEntry;
2938 CACHEFILE_ENTRY * pEntry;
2943 TRACE("(%s)\n", debugstr_w(lpszUrlName));
2945 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2946 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2949 SetLastError(ERROR_OUTOFMEMORY);
2952 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2954 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2955 if (error != ERROR_SUCCESS)
2957 HeapFree(GetProcessHeap(), 0, urlA);
2958 SetLastError(error);
2962 error = URLCacheContainer_OpenIndex(pContainer);
2963 if (error != ERROR_SUCCESS)
2965 HeapFree(GetProcessHeap(), 0, urlA);
2966 SetLastError(error);
2970 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2972 HeapFree(GetProcessHeap(), 0, urlA);
2976 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2978 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2979 TRACE("entry %s not found!\n", debugstr_a(urlA));
2980 HeapFree(GetProcessHeap(), 0, urlA);
2981 SetLastError(ERROR_FILE_NOT_FOUND);
2985 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2986 URLCache_DeleteEntry(pHeader, pEntry);
2988 URLCache_DeleteEntryFromHash(pHashEntry);
2990 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2992 HeapFree(GetProcessHeap(), 0, urlA);
2996 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2998 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3002 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3004 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3008 /***********************************************************************
3009 * CreateCacheContainerA (WININET.@)
3011 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3012 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3014 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3015 d1, d2, d3, d4, d5, d6, d7, d8);
3019 /***********************************************************************
3020 * CreateCacheContainerW (WININET.@)
3022 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3023 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3025 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3026 d1, d2, d3, d4, d5, d6, d7, d8);
3030 /***********************************************************************
3031 * FindFirstUrlCacheContainerA (WININET.@)
3033 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3035 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3039 /***********************************************************************
3040 * FindFirstUrlCacheContainerW (WININET.@)
3042 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3044 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3048 /***********************************************************************
3049 * FindNextUrlCacheContainerA (WININET.@)
3051 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3053 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3057 /***********************************************************************
3058 * FindNextUrlCacheContainerW (WININET.@)
3060 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3062 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3066 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3067 LPCSTR lpszUrlSearchPattern,
3071 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3072 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3074 LPDWORD pcbReserved2,
3078 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3079 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3080 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3081 SetLastError(ERROR_FILE_NOT_FOUND);
3085 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3086 LPCWSTR lpszUrlSearchPattern,
3090 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3091 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3093 LPDWORD pcbReserved2,
3097 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3098 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3099 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3100 SetLastError(ERROR_FILE_NOT_FOUND);
3104 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3106 typedef struct URLCacheFindEntryHandle
3109 LPWSTR lpszUrlSearchPattern;
3110 DWORD dwContainerIndex;
3111 DWORD dwHashTableIndex;
3112 DWORD dwHashEntryIndex;
3113 } URLCacheFindEntryHandle;
3115 /***********************************************************************
3116 * FindFirstUrlCacheEntryA (WININET.@)
3119 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3120 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3122 URLCacheFindEntryHandle *pEntryHandle;
3124 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3126 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3130 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3131 if (lpszUrlSearchPattern)
3133 int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
3134 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3135 if (!pEntryHandle->lpszUrlSearchPattern)
3137 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3140 MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
3143 pEntryHandle->lpszUrlSearchPattern = NULL;
3144 pEntryHandle->dwContainerIndex = 0;
3145 pEntryHandle->dwHashTableIndex = 0;
3146 pEntryHandle->dwHashEntryIndex = 0;
3148 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3150 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3153 return pEntryHandle;
3156 /***********************************************************************
3157 * FindFirstUrlCacheEntryW (WININET.@)
3160 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3161 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3163 URLCacheFindEntryHandle *pEntryHandle;
3165 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3167 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3171 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3172 if (lpszUrlSearchPattern)
3174 int len = strlenW(lpszUrlSearchPattern);
3175 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3176 if (!pEntryHandle->lpszUrlSearchPattern)
3178 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3181 memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3184 pEntryHandle->lpszUrlSearchPattern = NULL;
3185 pEntryHandle->dwContainerIndex = 0;
3186 pEntryHandle->dwHashTableIndex = 0;
3187 pEntryHandle->dwHashEntryIndex = 0;
3189 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3191 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3194 return pEntryHandle;
3197 /***********************************************************************
3198 * FindNextUrlCacheEntryA (WININET.@)
3200 BOOL WINAPI FindNextUrlCacheEntryA(
3202 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3203 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3205 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3206 URLCACHECONTAINER * pContainer;
3208 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3210 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3212 SetLastError(ERROR_INVALID_HANDLE);
3216 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3217 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3219 LPURLCACHE_HEADER pHeader;
3220 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3223 error = URLCacheContainer_OpenIndex(pContainer);
3224 if (error != ERROR_SUCCESS)
3226 SetLastError(error);
3230 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3233 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3234 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3236 const struct _HASH_ENTRY *pHashEntry = NULL;
3237 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3238 pEntryHandle->dwHashEntryIndex++)
3240 const URL_CACHEFILE_ENTRY *pUrlEntry;
3241 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3243 if (pEntry->dwSignature != URL_SIGNATURE)
3246 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3247 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3248 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3250 error = URLCache_CopyEntry(
3253 lpNextCacheEntryInfo,
3254 lpdwNextCacheEntryInfoBufferSize,
3256 FALSE /* not UNICODE */);
3257 if (error != ERROR_SUCCESS)
3259 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3260 SetLastError(error);
3263 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3265 /* increment the current index so that next time the function
3266 * is called the next entry is returned */
3267 pEntryHandle->dwHashEntryIndex++;
3268 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3273 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3276 SetLastError(ERROR_NO_MORE_ITEMS);
3280 /***********************************************************************
3281 * FindNextUrlCacheEntryW (WININET.@)
3283 BOOL WINAPI FindNextUrlCacheEntryW(
3285 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3286 LPDWORD lpdwNextCacheEntryInfoBufferSize
3289 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3290 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3294 /***********************************************************************
3295 * FindCloseUrlCache (WININET.@)
3297 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3299 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3301 TRACE("(%p)\n", hEnumHandle);
3303 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3305 SetLastError(ERROR_INVALID_HANDLE);
3309 pEntryHandle->dwMagic = 0;
3310 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3311 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3316 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3317 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3319 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3320 dwSearchCondition, lpGroupId, lpReserved);
3324 BOOL WINAPI FindNextUrlCacheEntryExA(
3326 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3327 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3329 LPDWORD pcbReserved2,
3333 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3334 lpReserved, pcbReserved2, lpReserved3);
3338 BOOL WINAPI FindNextUrlCacheEntryExW(
3340 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3341 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3343 LPDWORD pcbReserved2,
3347 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3348 lpReserved, pcbReserved2, lpReserved3);
3352 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3354 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3358 /***********************************************************************
3359 * CreateUrlCacheGroup (WININET.@)
3362 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3364 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3368 /***********************************************************************
3369 * DeleteUrlCacheGroup (WININET.@)
3372 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3374 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3375 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3379 /***********************************************************************
3380 * SetUrlCacheEntryGroupA (WININET.@)
3383 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3384 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3387 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3388 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3389 pbGroupAttributes, cbGroupAttributes, lpReserved);
3390 SetLastError(ERROR_FILE_NOT_FOUND);
3394 /***********************************************************************
3395 * SetUrlCacheEntryGroupW (WININET.@)
3398 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3399 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3402 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3403 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3404 pbGroupAttributes, cbGroupAttributes, lpReserved);
3405 SetLastError(ERROR_FILE_NOT_FOUND);
3409 /***********************************************************************
3410 * GetUrlCacheConfigInfoW (WININET.@)
3412 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3414 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3415 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3419 /***********************************************************************
3420 * GetUrlCacheConfigInfoA (WININET.@)
3422 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3424 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3426 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3427 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3431 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3432 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3433 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3435 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3436 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3437 lpdwGroupInfo, lpReserved);
3441 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3442 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3443 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3445 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3446 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3447 lpdwGroupInfo, lpReserved);
3451 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3452 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3454 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3455 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3459 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3460 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3462 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3463 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3467 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3469 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3473 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3475 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3479 /***********************************************************************
3480 * DeleteIE3Cache (WININET.@)
3482 * Deletes the files used by the IE3 URL caching system.
3485 * hWnd [I] A dummy window.
3486 * hInst [I] Instance of process calling the function.
3487 * lpszCmdLine [I] Options used by function.
3488 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3490 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3492 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3496 /***********************************************************************
3497 * IsUrlCacheEntryExpiredA (WININET.@)
3501 * dwFlags [I] Unknown
3502 * pftLastModified [O] Last modified time
3504 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3506 LPURLCACHE_HEADER pHeader;
3507 struct _HASH_ENTRY * pHashEntry;
3508 const CACHEFILE_ENTRY * pEntry;
3509 const URL_CACHEFILE_ENTRY * pUrlEntry;
3510 URLCACHECONTAINER * pContainer;
3513 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3515 error = URLCacheContainers_FindContainerA(url, &pContainer);
3516 if (error != ERROR_SUCCESS)
3518 SetLastError(error);
3522 error = URLCacheContainer_OpenIndex(pContainer);
3523 if (error != ERROR_SUCCESS)
3525 SetLastError(error);
3529 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3532 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3534 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3535 TRACE("entry %s not found!\n", url);
3536 SetLastError(ERROR_FILE_NOT_FOUND);
3540 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3541 if (pEntry->dwSignature != URL_SIGNATURE)
3543 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3544 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3545 SetLastError(ERROR_FILE_NOT_FOUND);
3549 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3551 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3553 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3558 /***********************************************************************
3559 * IsUrlCacheEntryExpiredW (WININET.@)
3563 * dwFlags [I] Unknown
3564 * pftLastModified [O] Last modified time
3566 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3568 LPURLCACHE_HEADER pHeader;
3569 struct _HASH_ENTRY * pHashEntry;
3570 const CACHEFILE_ENTRY * pEntry;
3571 const URL_CACHEFILE_ENTRY * pUrlEntry;
3572 URLCACHECONTAINER * pContainer;
3575 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3577 error = URLCacheContainers_FindContainerW(url, &pContainer);
3578 if (error != ERROR_SUCCESS)
3580 SetLastError(error);
3584 error = URLCacheContainer_OpenIndex(pContainer);
3585 if (error != ERROR_SUCCESS)
3587 SetLastError(error);
3591 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3594 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3596 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3597 TRACE("entry %s not found!\n", debugstr_w(url));
3598 SetLastError(ERROR_FILE_NOT_FOUND);
3602 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3603 if (pEntry->dwSignature != URL_SIGNATURE)
3605 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3606 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3607 SetLastError(ERROR_FILE_NOT_FOUND);
3611 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3613 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3615 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3620 /***********************************************************************
3621 * GetDiskInfoA (WININET.@)
3623 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3626 ULARGE_INTEGER bytes_free, bytes_total;
3628 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3632 SetLastError(ERROR_INVALID_PARAMETER);
3636 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3638 if (cluster_size) *cluster_size = 1;
3639 if (free) *free = bytes_free.QuadPart;
3640 if (total) *total = bytes_total.QuadPart;
3645 /***********************************************************************
3646 * RegisterUrlCacheNotification (WININET.@)
3648 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3650 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3654 /***********************************************************************
3655 * IncrementUrlCacheHeaderData (WININET.@)
3657 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3659 FIXME("(%u, %p)\n", index, data);