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"
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
53 #define ENTRY_START_OFFSET 0x4000
56 #define HASHTABLE_SIZE 448
57 #define HASHTABLE_BLOCKSIZE 7
58 #define HASHTABLE_FREE 3
59 #define ALLOCATION_TABLE_OFFSET 0x250
60 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
61 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
62 #define NEWFILE_NUM_BLOCKS 0xd80
63 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
65 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
66 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
67 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
68 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
69 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
71 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
73 typedef struct _CACHEFILE_ENTRY
77 DWORD dwSignature; /* e.g. "URL " */
78 /* CHAR szSignature[4];
80 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
83 typedef struct _URL_CACHEFILE_ENTRY
85 CACHEFILE_ENTRY CacheFileEntry;
86 FILETIME LastModifiedTime;
87 FILETIME LastAccessTime;
88 WORD wExpiredDate; /* expire date in dos format */
89 WORD wExpiredTime; /* expire time in dos format */
90 DWORD dwUnknown1; /* usually zero */
91 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
92 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
93 DWORD dwUnknown2; /* usually zero */
94 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
95 DWORD dwUnknown3; /* usually 0x60 */
96 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
97 BYTE CacheDir; /* index of cache directory this url is stored in */
98 BYTE Unknown4; /* usually zero */
99 WORD wUnknown5; /* usually 0x1010 */
100 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
101 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
102 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
103 DWORD dwHeaderInfoSize;
104 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
105 WORD wLastSyncDate; /* last sync date in dos format */
106 WORD wLastSyncTime; /* last sync time in dos format */
107 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
108 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109 WORD wUnknownDate; /* usually same as wLastSyncDate */
110 WORD wUnknownTime; /* usually same as wLastSyncTime */
111 DWORD dwUnknown7; /* usually zero */
112 DWORD dwUnknown8; /* usually zero */
113 /* packing to dword align start of next field */
114 /* CHAR szSourceUrlName[]; (url) */
115 /* packing to dword align start of next field */
116 /* CHAR szLocalFileName[]; (local file name excluding path) */
117 /* packing to dword align start of next field */
118 /* CHAR szHeaderInfo[]; (header info) */
119 } URL_CACHEFILE_ENTRY;
127 typedef struct _HASH_CACHEFILE_ENTRY
129 CACHEFILE_ENTRY CacheFileEntry;
131 DWORD dwHashTableNumber;
132 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
133 } HASH_CACHEFILE_ENTRY;
135 typedef struct _DIRECTORY_DATA
138 char filename[DIR_LENGTH];
141 typedef struct _URLCACHE_HEADER
143 char szSignature[28];
145 DWORD dwOffsetFirstHashTable;
146 DWORD dwIndexCapacityInBlocks;
149 DWORD dwCacheLimitLow; /* disk space limit for cache */
150 DWORD dwCacheLimitHigh; /* disk space limit for cache */
151 DWORD dwUnknown4; /* current disk space usage for cache */
152 DWORD dwUnknown5; /* current disk space usage for cache */
153 DWORD dwUnknown6; /* possibly a flag? */
155 BYTE DirectoryCount; /* number of directory_data's */
156 BYTE Unknown8[3]; /* just padding? */
157 DIRECTORY_DATA directory_data[1]; /* first directory entry */
158 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
159 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
161 typedef struct _STREAM_HANDLE
167 typedef struct _URLCACHECONTAINER
169 struct list entry; /* part of a list */
170 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
171 LPWSTR path; /* path to url container directory */
172 HANDLE hMapping; /* handle of file mapping */
173 DWORD file_size; /* size of file when mapping was opened */
174 HANDLE hMutex; /* handle of mutex */
178 /* List of all containers available */
179 static struct list UrlContainers = LIST_INIT(UrlContainers);
181 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
183 /***********************************************************************
184 * URLCache_PathToObjectName (Internal)
186 * Converts a path to a name suitable for use as a Win32 object name.
187 * Replaces '\\' characters in-place with the specified character
188 * (usually '_' or '!')
194 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
196 for (; *lpszPath; lpszPath++)
198 if (*lpszPath == '\\')
203 /***********************************************************************
204 * URLCacheContainer_OpenIndex (Internal)
206 * Opens the index file and saves mapping handle in hCacheIndexMapping
209 * ERROR_SUCCESS if succeeded
210 * Any other Win32 error code if failed
213 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
216 WCHAR wszFilePath[MAX_PATH];
219 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
220 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
222 if (pContainer->hMapping)
223 return ERROR_SUCCESS;
225 strcpyW(wszFilePath, pContainer->path);
226 strcatW(wszFilePath, wszIndex);
228 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
229 if (hFile == INVALID_HANDLE_VALUE)
231 /* Maybe the directory wasn't there? Try to create it */
232 if (CreateDirectoryW(pContainer->path, 0))
233 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
235 if (hFile == INVALID_HANDLE_VALUE)
237 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
238 return GetLastError();
241 /* At this stage we need the mutex because we may be about to create the
244 WaitForSingleObject(pContainer->hMutex, INFINITE);
246 dwFileSize = GetFileSize(hFile, NULL);
247 if (dwFileSize == INVALID_FILE_SIZE)
249 ReleaseMutex(pContainer->hMutex);
250 return GetLastError();
255 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
257 char achZeroes[0x1000];
259 DWORD dwError = ERROR_SUCCESS;
261 /* Write zeroes to the entire file so we can safely map it without
262 * fear of getting a SEGV because the disk is full.
264 memset(achZeroes, 0, sizeof(achZeroes));
265 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
267 DWORD dwWrite = sizeof(achZeroes);
270 if (NEWFILE_SIZE - dwOffset < dwWrite)
271 dwWrite = NEWFILE_SIZE - dwOffset;
272 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
273 dwWritten != dwWrite)
275 /* If we fail to write, we need to return the error that
276 * cause the problem and also make sure the file is no
277 * longer there, if possible.
279 dwError = GetLastError();
285 if (dwError == ERROR_SUCCESS)
287 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
291 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
296 WCHAR wszDirPath[MAX_PATH];
299 HASH_CACHEFILE_ENTRY *pHashEntry;
301 dwFileSize = NEWFILE_SIZE;
303 /* First set some constants and defaults in the header */
304 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
305 pHeader->dwFileSize = dwFileSize;
306 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
307 /* 127MB - taken from default for Windows 2000 */
308 pHeader->dwCacheLimitHigh = 0;
309 pHeader->dwCacheLimitLow = 0x07ff5400;
310 /* Copied from a Windows 2000 cache index */
311 pHeader->DirectoryCount = 4;
313 /* If the registry has a cache size set, use the registry value */
314 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
317 DWORD len = sizeof(dw);
320 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
321 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
322 keytype == REG_DWORD)
324 pHeader->dwCacheLimitHigh = (dw >> 22);
325 pHeader->dwCacheLimitLow = dw << 10;
330 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
332 /* Last step - create the directories */
334 strcpyW(wszDirPath, pContainer->path);
335 pwchDir = wszDirPath + strlenW(wszDirPath);
338 GetSystemTimeAsFileTime(&ft);
340 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
342 /* The following values were copied from a Windows index.
343 * I don't know what the values are supposed to mean but
344 * have made them the same in the hope that this will
345 * be better for compatibility
347 pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
351 ULONGLONG n = ft.dwHighDateTime;
353 /* Generate a file name to attempt to create.
354 * This algorithm will create what will appear
355 * to be random and unrelated directory names
356 * of up to 9 characters in length.
359 n += ft.dwLowDateTime;
360 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
362 for (k = 0; k < 8; ++k)
366 /* Dividing by a prime greater than 36 helps
367 * with the appearance of randomness
372 pwchDir[k] = '0' + r;
374 pwchDir[k] = 'A' + (r - 10);
377 if (CreateDirectoryW(wszDirPath, 0))
379 /* The following is OK because we generated an
380 * 8 character directory name made from characters
381 * [A-Z0-9], which are equivalent for all code
382 * pages and for UTF-16
384 for (k = 0; k < 8; ++k)
385 pHeader->directory_data[i].filename[k] = pwchDir[k];
390 /* Give up. The most likely cause of this
391 * is a full disk, but whatever the cause
392 * is, it should be more than apparent that
395 dwError = GetLastError();
401 UnmapViewOfFile(pHeader);
405 dwError = GetLastError();
407 CloseHandle(hMapping);
411 dwError = GetLastError();
418 DeleteFileW(wszFilePath);
419 ReleaseMutex(pContainer->hMutex);
425 ReleaseMutex(pContainer->hMutex);
427 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
428 URLCache_PathToObjectName(wszFilePath, '_');
429 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
430 if (!pContainer->hMapping)
431 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
433 if (!pContainer->hMapping)
435 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
436 return GetLastError();
439 return ERROR_SUCCESS;
442 /***********************************************************************
443 * URLCacheContainer_CloseIndex (Internal)
451 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
453 CloseHandle(pContainer->hMapping);
454 pContainer->hMapping = NULL;
457 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
459 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
460 int path_len = strlenW(path);
461 int cache_prefix_len = strlenW(cache_prefix);
468 pContainer->hMapping = NULL;
469 pContainer->file_size = 0;
471 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
472 if (!pContainer->path)
474 HeapFree(GetProcessHeap(), 0, pContainer);
478 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
480 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
481 if (!pContainer->cache_prefix)
483 HeapFree(GetProcessHeap(), 0, pContainer->path);
484 HeapFree(GetProcessHeap(), 0, pContainer);
488 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
490 CharLowerW(mutex_name);
491 URLCache_PathToObjectName(mutex_name, '!');
493 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
495 ERR("couldn't create mutex (error is %d)\n", GetLastError());
496 HeapFree(GetProcessHeap(), 0, pContainer->path);
497 HeapFree(GetProcessHeap(), 0, pContainer);
501 list_add_head(&UrlContainers, &pContainer->entry);
506 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
508 list_remove(&pContainer->entry);
510 URLCacheContainer_CloseIndex(pContainer);
511 CloseHandle(pContainer->hMutex);
512 HeapFree(GetProcessHeap(), 0, pContainer->path);
513 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
514 HeapFree(GetProcessHeap(), 0, pContainer);
517 void URLCacheContainers_CreateDefaults(void)
519 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
520 static const WCHAR UrlPrefix[] = {0};
521 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
522 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
523 static const WCHAR CookieSuffix[] = {0};
524 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
527 int nFolder; /* CSIDL_* constant */
528 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
529 const WCHAR * cache_prefix; /* prefix used to reference the container */
530 } DefaultContainerData[] =
532 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
533 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
534 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
538 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
540 WCHAR wszCachePath[MAX_PATH];
541 WCHAR wszMutexName[MAX_PATH];
542 int path_len, suffix_len;
544 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
546 ERR("Couldn't get path for default container %u\n", i);
549 path_len = strlenW(wszCachePath);
550 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
552 if (path_len + suffix_len + 2 > MAX_PATH)
554 ERR("Path too long\n");
558 wszCachePath[path_len] = '\\';
559 wszCachePath[path_len+1] = 0;
561 strcpyW(wszMutexName, wszCachePath);
565 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
566 wszCachePath[path_len + suffix_len + 1] = '\\';
567 wszCachePath[path_len + suffix_len + 2] = '\0';
570 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
574 void URLCacheContainers_DeleteAll(void)
576 while(!list_empty(&UrlContainers))
577 URLCacheContainer_DeleteContainer(
578 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
582 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
584 URLCACHECONTAINER * pContainer;
586 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
588 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
590 int prefix_len = strlenW(pContainer->cache_prefix);
591 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
593 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
594 *ppContainer = pContainer;
595 return ERROR_SUCCESS;
598 ERR("no container found\n");
599 return ERROR_FILE_NOT_FOUND;
602 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
606 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
607 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
609 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
610 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
611 HeapFree(GetProcessHeap(), 0, lpwszUrl);
614 return GetLastError();
617 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
620 URLCACHECONTAINER * pContainer;
622 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
624 /* non-NULL search pattern only returns one container ever */
625 if (lpwszSearchPattern && dwIndex > 0)
628 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
630 if (lpwszSearchPattern)
632 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
634 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
635 *ppContainer = pContainer;
643 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
644 *ppContainer = pContainer;
653 /***********************************************************************
654 * URLCacheContainer_LockIndex (Internal)
656 * Locks the index for system-wide exclusive access.
659 * Cache file header if successful
660 * NULL if failed and calls SetLastError.
662 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
666 URLCACHE_HEADER * pHeader;
670 WaitForSingleObject(pContainer->hMutex, INFINITE);
672 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
676 ReleaseMutex(pContainer->hMutex);
677 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
680 pHeader = (URLCACHE_HEADER *)pIndexData;
682 /* file has grown - we need to remap to prevent us getting
683 * access violations when we try and access beyond the end
684 * of the memory mapped file */
685 if (pHeader->dwFileSize != pContainer->file_size)
687 URLCacheContainer_CloseIndex(pContainer);
688 error = URLCacheContainer_OpenIndex(pContainer);
689 if (error != ERROR_SUCCESS)
691 ReleaseMutex(pContainer->hMutex);
695 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
699 ReleaseMutex(pContainer->hMutex);
700 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
703 pHeader = (URLCACHE_HEADER *)pIndexData;
706 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
708 for (index = 0; index < pHeader->DirectoryCount; index++)
710 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
716 /***********************************************************************
717 * URLCacheContainer_UnlockIndex (Internal)
720 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
723 ReleaseMutex(pContainer->hMutex);
724 return UnmapViewOfFile(pHeader);
729 #define CHAR_BIT (8 * sizeof(CHAR))
732 /***********************************************************************
733 * URLCache_Allocation_BlockIsFree (Internal)
735 * Is the specified block number free?
742 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
744 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
745 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
748 /***********************************************************************
749 * URLCache_Allocation_BlockFree (Internal)
751 * Marks the specified block as free
757 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
759 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
760 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
763 /***********************************************************************
764 * URLCache_Allocation_BlockAllocate (Internal)
766 * Marks the specified block as allocated
772 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
774 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
775 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
778 /***********************************************************************
779 * URLCache_FindFirstFreeEntry (Internal)
781 * Finds and allocates the first block of free space big enough and
782 * sets ppEntry to point to it.
785 * TRUE if it had enough space
786 * FALSE if it couldn't find enough space
789 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
791 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
794 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
796 for (dwFreeCounter = 0;
797 dwFreeCounter < dwBlocksNeeded &&
798 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
799 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
801 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
803 if (dwFreeCounter == dwBlocksNeeded)
806 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
807 for (index = 0; index < dwBlocksNeeded; index++)
808 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
809 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
810 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
814 FIXME("Grow file\n");
818 /***********************************************************************
819 * URLCache_DeleteEntry (Internal)
821 * Deletes the specified entry and frees the space allocated to it
824 * TRUE if it succeeded
828 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
832 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
834 /* update allocation table */
835 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
836 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
837 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
839 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
843 /***********************************************************************
844 * URLCache_LocalFileNameToPathW (Internal)
846 * Copies the full path to the specified buffer given the local file
847 * name and the index of the directory it is in. Always sets value in
848 * lpBufferSize to the required buffer size (in bytes).
851 * TRUE if the buffer was big enough
852 * FALSE if the buffer was too small
855 static BOOL URLCache_LocalFileNameToPathW(
856 const URLCACHECONTAINER * pContainer,
857 LPCURLCACHE_HEADER pHeader,
858 LPCSTR szLocalFileName,
864 int path_len = strlenW(pContainer->path);
865 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
866 if (Directory >= pHeader->DirectoryCount)
872 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
873 if (nRequired < *lpBufferSize)
877 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
878 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
879 wszPath[dir_len + path_len] = '\\';
880 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
881 *lpBufferSize = nRequired;
884 *lpBufferSize = nRequired;
888 /***********************************************************************
889 * URLCache_LocalFileNameToPathA (Internal)
891 * Copies the full path to the specified buffer given the local file
892 * name and the index of the directory it is in. Always sets value in
893 * lpBufferSize to the required buffer size.
896 * TRUE if the buffer was big enough
897 * FALSE if the buffer was too small
900 static BOOL URLCache_LocalFileNameToPathA(
901 const URLCACHECONTAINER * pContainer,
902 LPCURLCACHE_HEADER pHeader,
903 LPCSTR szLocalFileName,
909 int path_len, file_name_len, dir_len;
911 if (Directory >= pHeader->DirectoryCount)
917 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
918 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
919 dir_len = DIR_LENGTH;
921 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
922 if (nRequired < *lpBufferSize)
924 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
925 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
926 szPath[path_len + dir_len] = '\\';
927 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
928 *lpBufferSize = nRequired;
931 *lpBufferSize = nRequired;
935 /***********************************************************************
936 * URLCache_CopyEntry (Internal)
938 * Copies an entry from the cache index file to the Win32 structure
941 * ERROR_SUCCESS if the buffer was big enough
942 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
945 static DWORD URLCache_CopyEntry(
946 URLCACHECONTAINER * pContainer,
947 LPCURLCACHE_HEADER pHeader,
948 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
949 LPDWORD lpdwBufferSize,
950 const URL_CACHEFILE_ENTRY * pUrlEntry,
954 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
956 if (*lpdwBufferSize >= dwRequiredSize)
958 lpCacheEntryInfo->lpHeaderInfo = NULL;
959 lpCacheEntryInfo->lpszFileExtension = NULL;
960 lpCacheEntryInfo->lpszLocalFileName = NULL;
961 lpCacheEntryInfo->lpszSourceUrlName = NULL;
962 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
963 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
964 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
965 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
966 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
967 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
968 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
969 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
970 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
971 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
972 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
973 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
974 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
975 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
978 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
979 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
980 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
982 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
984 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
985 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
987 /* FIXME: is source url optional? */
988 if (*lpdwBufferSize >= dwRequiredSize)
990 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
992 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
994 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
997 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
998 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
999 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1001 if (pUrlEntry->dwOffsetLocalName)
1003 LONG nLocalFilePathSize;
1004 LPSTR lpszLocalFileName;
1005 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1006 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1007 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1008 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1010 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1012 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1014 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1015 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1016 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1018 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1020 if (*lpdwBufferSize >= dwRequiredSize)
1022 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1023 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1024 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1026 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1027 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1028 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1030 if (pUrlEntry->dwOffsetFileExtension)
1035 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1037 lenExtension = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1038 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1040 if (*lpdwBufferSize >= dwRequiredSize)
1042 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1044 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1046 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1049 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1050 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1051 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1054 if (dwRequiredSize > *lpdwBufferSize)
1056 *lpdwBufferSize = dwRequiredSize;
1057 return ERROR_INSUFFICIENT_BUFFER;
1059 *lpdwBufferSize = dwRequiredSize;
1060 return ERROR_SUCCESS;
1064 /***********************************************************************
1065 * URLCache_SetEntryInfo (Internal)
1067 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1068 * according to the flags set by dwFieldControl.
1071 * ERROR_SUCCESS if the buffer was big enough
1072 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1075 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1077 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1078 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1079 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1080 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1081 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1082 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1083 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1084 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1085 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1086 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1087 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1088 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1089 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1090 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1091 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1092 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1094 return ERROR_SUCCESS;
1097 /***********************************************************************
1098 * URLCache_HashKey (Internal)
1100 * Returns the hash key for a given string
1103 * hash key for the string
1106 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1108 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1109 * but the algorithm and result are not the same!
1111 static const unsigned char lookupTable[256] =
1113 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1114 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1115 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1116 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1117 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1118 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1119 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1120 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1121 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1122 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1123 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1124 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1125 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1126 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1127 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1128 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1129 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1130 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1131 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1132 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1133 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1134 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1135 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1136 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1137 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1138 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1139 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1140 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1141 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1142 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1143 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1144 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1149 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1150 key[i] = lookupTable[i];
1152 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1154 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1155 key[i] = lookupTable[*lpszKey ^ key[i]];
1158 return *(DWORD *)key;
1161 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1163 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1166 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1168 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1169 return ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) &&
1170 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1173 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1175 /* structure of hash table:
1176 * 448 entries divided into 64 blocks
1177 * each block therefore contains a chain of 7 key/offset pairs
1178 * how position in table is calculated:
1179 * 1. the url is hashed in helper function
1180 * 2. the key % 64 * 8 is the offset
1181 * 3. the key in the hash table is the hash key aligned to 64
1184 * there can be multiple hash tables in the file and the offset to
1185 * the next one is stored in the header of the hash table
1187 DWORD key = URLCache_HashKey(lpszUrl);
1188 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1189 HASH_CACHEFILE_ENTRY * pHashEntry;
1190 DWORD dwHashTableNumber = 0;
1192 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1194 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1195 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1196 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1199 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1201 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1204 /* make sure that it is in fact a hash entry */
1205 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1207 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1211 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1213 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1214 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1216 /* FIXME: we should make sure that this is the right element
1217 * before returning and claiming that it is. We can do this
1218 * by doing a simple compare between the URL we were given
1219 * and the URL stored in the entry. However, this assumes
1220 * we know the format of all the entries stored in the
1222 *ppHashEntry = pHashElement;
1230 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1236 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1237 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1240 SetLastError(ERROR_OUTOFMEMORY);
1243 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, urlA, url_len, NULL, NULL);
1244 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1245 HeapFree(GetProcessHeap(), 0, urlA);
1249 /***********************************************************************
1250 * URLCache_HashEntrySetUse (Internal)
1252 * Searches all the hash tables in the index for the given URL and
1253 * sets the use count (stored or'ed with key)
1256 * TRUE if the entry was found
1257 * FALSE if the entry could not be found
1260 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1262 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1266 /***********************************************************************
1267 * URLCache_DeleteEntryFromHash (Internal)
1269 * Searches all the hash tables in the index for the given URL and
1270 * then if found deletes the entry.
1273 * TRUE if the entry was found
1274 * FALSE if the entry could not be found
1277 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1279 pHashEntry->dwHashKey = HASHTABLE_FREE;
1280 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1284 /***********************************************************************
1285 * URLCache_AddEntryToHash (Internal)
1287 * Searches all the hash tables for a free slot based on the offset
1288 * generated from the hash key. If a free slot is found, the offset and
1289 * key are entered into the hash table.
1292 * ERROR_SUCCESS if the entry was added
1293 * Any other Win32 error code if the entry could not be added
1296 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1298 /* see URLCache_FindEntryInHash for structure of hash tables */
1300 DWORD key = URLCache_HashKey(lpszUrl);
1301 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1302 HASH_CACHEFILE_ENTRY * pHashEntry;
1303 DWORD dwHashTableNumber = 0;
1306 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1308 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1309 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1310 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1313 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1315 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1318 /* make sure that it is in fact a hash entry */
1319 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1321 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1325 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1327 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1328 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1330 pHashElement->dwHashKey = key;
1331 pHashElement->dwOffsetEntry = dwOffsetEntry;
1332 return ERROR_SUCCESS;
1336 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1337 if (error != ERROR_SUCCESS)
1340 pHashEntry->HashTable[offset].dwHashKey = key;
1341 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1342 return ERROR_SUCCESS;
1345 /***********************************************************************
1346 * URLCache_CreateHashTable (Internal)
1348 * Creates a new hash table in free space and adds it to the chain of existing
1352 * ERROR_SUCCESS if the hash table was created
1353 * ERROR_DISK_FULL if the hash table could not be created
1356 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1361 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1363 FIXME("no free space for hash table\n");
1364 return ERROR_DISK_FULL;
1367 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1370 pPrevHash->dwAddressNext = dwOffset;
1372 pHeader->dwOffsetFirstHashTable = dwOffset;
1373 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1374 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1375 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1376 for (i = 0; i < HASHTABLE_SIZE; i++)
1378 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1379 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1381 return ERROR_SUCCESS;
1384 /***********************************************************************
1385 * URLCache_EnumHashTables (Internal)
1387 * Enumerates the hash tables in a container.
1390 * TRUE if an entry was found
1391 * FALSE if there are no more tables to enumerate.
1394 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1396 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1397 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1398 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1400 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1401 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1403 /* make sure that it is in fact a hash entry */
1404 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1406 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1407 (*pdwHashTableNumber)++;
1411 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1417 /***********************************************************************
1418 * URLCache_EnumHashTableEntries (Internal)
1420 * Enumerates entries in a hash table and returns the next non-free entry.
1423 * TRUE if an entry was found
1424 * FALSE if the hash table is empty or there are no more entries to
1428 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1429 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1431 for (; *index < HASHTABLE_SIZE ; (*index)++)
1433 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1436 *ppHashEntry = &pHashEntry->HashTable[*index];
1437 TRACE("entry found %d\n", *index);
1440 TRACE("no more entries (%d)\n", *index);
1444 /***********************************************************************
1445 * GetUrlCacheEntryInfoExA (WININET.@)
1448 BOOL WINAPI GetUrlCacheEntryInfoExA(
1450 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1451 LPDWORD lpdwCacheEntryInfoBufSize,
1453 LPDWORD lpdwReserved,
1457 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1458 debugstr_a(lpszUrl),
1460 lpdwCacheEntryInfoBufSize,
1466 if ((lpszReserved != NULL) ||
1467 (lpdwReserved != NULL) ||
1468 (lpReserved != NULL))
1470 ERR("Reserved value was not 0\n");
1471 SetLastError(ERROR_INVALID_PARAMETER);
1475 FIXME("Undocumented flag(s): %x\n", dwFlags);
1476 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1479 /***********************************************************************
1480 * GetUrlCacheEntryInfoA (WININET.@)
1483 BOOL WINAPI GetUrlCacheEntryInfoA(
1484 IN LPCSTR lpszUrlName,
1485 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1486 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1489 LPURLCACHE_HEADER pHeader;
1490 struct _HASH_ENTRY * pHashEntry;
1491 const CACHEFILE_ENTRY * pEntry;
1492 const URL_CACHEFILE_ENTRY * pUrlEntry;
1493 URLCACHECONTAINER * pContainer;
1496 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1498 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1499 if (error != ERROR_SUCCESS)
1501 SetLastError(error);
1505 error = URLCacheContainer_OpenIndex(pContainer);
1506 if (error != ERROR_SUCCESS)
1508 SetLastError(error);
1512 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1515 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1517 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1518 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1519 SetLastError(ERROR_FILE_NOT_FOUND);
1523 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1524 if (pEntry->dwSignature != URL_SIGNATURE)
1526 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1527 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1528 SetLastError(ERROR_FILE_NOT_FOUND);
1532 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1533 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1534 if (pUrlEntry->dwOffsetHeaderInfo)
1535 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1537 if (lpdwCacheEntryInfoBufferSize)
1539 if (!lpCacheEntryInfo)
1540 *lpdwCacheEntryInfoBufferSize = 0;
1542 error = URLCache_CopyEntry(
1546 lpdwCacheEntryInfoBufferSize,
1549 if (error != ERROR_SUCCESS)
1551 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1552 SetLastError(error);
1555 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1558 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1563 /***********************************************************************
1564 * GetUrlCacheEntryInfoW (WININET.@)
1567 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1568 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1569 LPDWORD lpdwCacheEntryInfoBufferSize)
1571 LPURLCACHE_HEADER pHeader;
1572 struct _HASH_ENTRY * pHashEntry;
1573 const CACHEFILE_ENTRY * pEntry;
1574 const URL_CACHEFILE_ENTRY * pUrlEntry;
1575 URLCACHECONTAINER * pContainer;
1578 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1580 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1581 if (error != ERROR_SUCCESS)
1583 SetLastError(error);
1587 error = URLCacheContainer_OpenIndex(pContainer);
1588 if (error != ERROR_SUCCESS)
1590 SetLastError(error);
1594 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1597 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1599 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1600 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1601 SetLastError(ERROR_FILE_NOT_FOUND);
1605 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1606 if (pEntry->dwSignature != URL_SIGNATURE)
1608 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1609 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1610 SetLastError(ERROR_FILE_NOT_FOUND);
1614 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1615 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1616 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1618 if (lpdwCacheEntryInfoBufferSize)
1620 if (!lpCacheEntryInfo)
1621 *lpdwCacheEntryInfoBufferSize = 0;
1623 error = URLCache_CopyEntry(
1626 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1627 lpdwCacheEntryInfoBufferSize,
1629 TRUE /* UNICODE */);
1630 if (error != ERROR_SUCCESS)
1632 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1633 SetLastError(error);
1636 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1639 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1644 /***********************************************************************
1645 * GetUrlCacheEntryInfoExW (WININET.@)
1648 BOOL WINAPI GetUrlCacheEntryInfoExW(
1650 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1651 LPDWORD lpdwCacheEntryInfoBufSize,
1652 LPWSTR lpszReserved,
1653 LPDWORD lpdwReserved,
1657 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1658 debugstr_w(lpszUrl),
1660 lpdwCacheEntryInfoBufSize,
1666 if ((lpszReserved != NULL) ||
1667 (lpdwReserved != NULL) ||
1668 (lpReserved != NULL))
1670 ERR("Reserved value was not 0\n");
1671 SetLastError(ERROR_INVALID_PARAMETER);
1675 FIXME("Undocumented flag(s): %x\n", dwFlags);
1676 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1679 /***********************************************************************
1680 * SetUrlCacheEntryInfoA (WININET.@)
1682 BOOL WINAPI SetUrlCacheEntryInfoA(
1684 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1685 DWORD dwFieldControl)
1687 LPURLCACHE_HEADER pHeader;
1688 struct _HASH_ENTRY * pHashEntry;
1689 CACHEFILE_ENTRY * pEntry;
1690 URLCACHECONTAINER * pContainer;
1693 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1695 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1696 if (error != ERROR_SUCCESS)
1698 SetLastError(error);
1702 error = URLCacheContainer_OpenIndex(pContainer);
1703 if (error != ERROR_SUCCESS)
1705 SetLastError(error);
1709 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1712 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1714 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1715 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1716 SetLastError(ERROR_FILE_NOT_FOUND);
1720 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1721 if (pEntry->dwSignature != URL_SIGNATURE)
1723 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1724 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1725 SetLastError(ERROR_FILE_NOT_FOUND);
1729 URLCache_SetEntryInfo(
1730 (URL_CACHEFILE_ENTRY *)pEntry,
1731 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1734 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1739 /***********************************************************************
1740 * SetUrlCacheEntryInfoW (WININET.@)
1742 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1744 LPURLCACHE_HEADER pHeader;
1745 struct _HASH_ENTRY * pHashEntry;
1746 CACHEFILE_ENTRY * pEntry;
1747 URLCACHECONTAINER * pContainer;
1750 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1752 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1753 if (error != ERROR_SUCCESS)
1755 SetLastError(error);
1759 error = URLCacheContainer_OpenIndex(pContainer);
1760 if (error != ERROR_SUCCESS)
1762 SetLastError(error);
1766 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1769 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1771 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1772 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1773 SetLastError(ERROR_FILE_NOT_FOUND);
1777 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1778 if (pEntry->dwSignature != URL_SIGNATURE)
1780 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1781 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1782 SetLastError(ERROR_FILE_NOT_FOUND);
1786 URLCache_SetEntryInfo(
1787 (URL_CACHEFILE_ENTRY *)pEntry,
1791 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1796 /***********************************************************************
1797 * RetrieveUrlCacheEntryFileA (WININET.@)
1800 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1801 IN LPCSTR lpszUrlName,
1802 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1803 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1807 LPURLCACHE_HEADER pHeader;
1808 struct _HASH_ENTRY * pHashEntry;
1809 CACHEFILE_ENTRY * pEntry;
1810 URL_CACHEFILE_ENTRY * pUrlEntry;
1811 URLCACHECONTAINER * pContainer;
1814 TRACE("(%s, %p, %p, 0x%08x)\n",
1815 debugstr_a(lpszUrlName),
1817 lpdwCacheEntryInfoBufferSize,
1820 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1821 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1823 SetLastError(ERROR_INVALID_PARAMETER);
1827 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1828 if (error != ERROR_SUCCESS)
1830 SetLastError(error);
1834 error = URLCacheContainer_OpenIndex(pContainer);
1835 if (error != ERROR_SUCCESS)
1837 SetLastError(error);
1841 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1844 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1846 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1847 TRACE("entry %s not found!\n", lpszUrlName);
1848 SetLastError(ERROR_FILE_NOT_FOUND);
1852 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1853 if (pEntry->dwSignature != URL_SIGNATURE)
1855 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1856 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1857 SetLastError(ERROR_FILE_NOT_FOUND);
1861 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1862 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1863 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1865 pUrlEntry->dwHitRate++;
1866 pUrlEntry->dwUseCount++;
1867 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1869 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
1870 lpdwCacheEntryInfoBufferSize, pUrlEntry,
1872 if (error != ERROR_SUCCESS)
1874 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1875 SetLastError(error);
1878 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1880 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1885 /***********************************************************************
1886 * RetrieveUrlCacheEntryFileW (WININET.@)
1889 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1890 IN LPCWSTR lpszUrlName,
1891 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1892 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1896 LPURLCACHE_HEADER pHeader;
1897 struct _HASH_ENTRY * pHashEntry;
1898 CACHEFILE_ENTRY * pEntry;
1899 URL_CACHEFILE_ENTRY * pUrlEntry;
1900 URLCACHECONTAINER * pContainer;
1903 TRACE("(%s, %p, %p, 0x%08x)\n",
1904 debugstr_w(lpszUrlName),
1906 lpdwCacheEntryInfoBufferSize,
1909 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1910 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1912 SetLastError(ERROR_INVALID_PARAMETER);
1916 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
1917 if (error != ERROR_SUCCESS)
1919 SetLastError(error);
1923 error = URLCacheContainer_OpenIndex(pContainer);
1924 if (error != ERROR_SUCCESS)
1926 SetLastError(error);
1930 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1933 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1935 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1936 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1937 SetLastError(ERROR_FILE_NOT_FOUND);
1941 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1942 if (pEntry->dwSignature != URL_SIGNATURE)
1944 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1945 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1946 SetLastError(ERROR_FILE_NOT_FOUND);
1950 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1951 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1952 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1954 pUrlEntry->dwHitRate++;
1955 pUrlEntry->dwUseCount++;
1956 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1958 error = URLCache_CopyEntry(
1961 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1962 lpdwCacheEntryInfoBufferSize,
1964 TRUE /* UNICODE */);
1965 if (error != ERROR_SUCCESS)
1967 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1968 SetLastError(error);
1971 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1978 /***********************************************************************
1979 * UnlockUrlCacheEntryFileA (WININET.@)
1982 BOOL WINAPI UnlockUrlCacheEntryFileA(
1983 IN LPCSTR lpszUrlName,
1987 LPURLCACHE_HEADER pHeader;
1988 struct _HASH_ENTRY * pHashEntry;
1989 CACHEFILE_ENTRY * pEntry;
1990 URL_CACHEFILE_ENTRY * pUrlEntry;
1991 URLCACHECONTAINER * pContainer;
1994 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1998 ERR("dwReserved != 0\n");
1999 SetLastError(ERROR_INVALID_PARAMETER);
2003 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2004 if (error != ERROR_SUCCESS)
2006 SetLastError(error);
2010 error = URLCacheContainer_OpenIndex(pContainer);
2011 if (error != ERROR_SUCCESS)
2013 SetLastError(error);
2017 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2020 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2022 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2023 TRACE("entry %s not found!\n", lpszUrlName);
2024 SetLastError(ERROR_FILE_NOT_FOUND);
2028 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2029 if (pEntry->dwSignature != URL_SIGNATURE)
2031 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2032 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2033 SetLastError(ERROR_FILE_NOT_FOUND);
2037 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2039 if (pUrlEntry->dwUseCount == 0)
2041 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2044 pUrlEntry->dwUseCount--;
2045 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2047 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2052 /***********************************************************************
2053 * UnlockUrlCacheEntryFileW (WININET.@)
2056 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2058 LPURLCACHE_HEADER pHeader;
2059 struct _HASH_ENTRY * pHashEntry;
2060 CACHEFILE_ENTRY * pEntry;
2061 URL_CACHEFILE_ENTRY * pUrlEntry;
2062 URLCACHECONTAINER * pContainer;
2065 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2069 ERR("dwReserved != 0\n");
2070 SetLastError(ERROR_INVALID_PARAMETER);
2074 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2075 if (error != ERROR_SUCCESS)
2077 SetLastError(error);
2081 error = URLCacheContainer_OpenIndex(pContainer);
2082 if (error != ERROR_SUCCESS)
2084 SetLastError(error);
2088 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2091 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2093 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2094 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2095 SetLastError(ERROR_FILE_NOT_FOUND);
2099 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2100 if (pEntry->dwSignature != URL_SIGNATURE)
2102 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2103 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2104 SetLastError(ERROR_FILE_NOT_FOUND);
2108 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2110 if (pUrlEntry->dwUseCount == 0)
2112 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2115 pUrlEntry->dwUseCount--;
2116 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2118 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2123 /***********************************************************************
2124 * CreateUrlCacheEntryA (WININET.@)
2127 BOOL WINAPI CreateUrlCacheEntryA(
2128 IN LPCSTR lpszUrlName,
2129 IN DWORD dwExpectedFileSize,
2130 IN LPCSTR lpszFileExtension,
2131 OUT LPSTR lpszFileName,
2137 WCHAR *file_extension;
2138 WCHAR file_name[MAX_PATH];
2139 BOOL bSuccess = FALSE;
2142 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2143 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2145 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2146 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2147 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2149 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2150 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2152 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2158 dwError = GetLastError();
2163 dwError = GetLastError();
2165 HeapFree(GetProcessHeap(), 0, file_extension);
2169 dwError = GetLastError();
2171 HeapFree(GetProcessHeap(), 0, url_name);
2173 SetLastError(dwError);
2177 /***********************************************************************
2178 * CreateUrlCacheEntryW (WININET.@)
2181 BOOL WINAPI CreateUrlCacheEntryW(
2182 IN LPCWSTR lpszUrlName,
2183 IN DWORD dwExpectedFileSize,
2184 IN LPCWSTR lpszFileExtension,
2185 OUT LPWSTR lpszFileName,
2189 URLCACHECONTAINER * pContainer;
2190 LPURLCACHE_HEADER pHeader;
2191 CHAR szFile[MAX_PATH];
2192 WCHAR szExtension[MAX_PATH];
2193 LPCWSTR lpszUrlPart;
2195 LPCWSTR lpszFileNameExtension;
2196 LPWSTR lpszFileNameNoPath;
2198 int countnoextension;
2201 BOOL bFound = FALSE;
2204 static const WCHAR szWWW[] = {'w','w','w',0};
2206 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2207 debugstr_w(lpszUrlName),
2209 debugstr_w(lpszFileExtension),
2215 ERR("dwReserved != 0\n");
2216 SetLastError(ERROR_INVALID_PARAMETER);
2220 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2222 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2225 for (lpszUrlPart = lpszUrlEnd;
2226 (lpszUrlPart >= lpszUrlName);
2229 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2235 else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2237 lpszUrlEnd = lpszUrlPart;
2240 if (!lstrcmpW(lpszUrlPart, szWWW))
2242 lpszUrlPart += lstrlenW(szWWW);
2245 count = lpszUrlEnd - lpszUrlPart;
2247 if (bFound && (count < MAX_PATH))
2249 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2253 /* FIXME: get rid of illegal characters like \, / and : */
2257 FIXME("need to generate a random filename\n");
2260 TRACE("File name: %s\n", debugstr_a(szFile));
2262 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2263 if (error != ERROR_SUCCESS)
2265 SetLastError(error);
2269 error = URLCacheContainer_OpenIndex(pContainer);
2270 if (error != ERROR_SUCCESS)
2272 SetLastError(error);
2276 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2279 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2281 lBufferSize = MAX_PATH * sizeof(WCHAR);
2282 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2284 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2286 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2287 lpszFileNameNoPath >= lpszFileName;
2288 --lpszFileNameNoPath)
2290 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2294 countnoextension = lstrlenW(lpszFileNameNoPath);
2295 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2296 if (lpszFileNameExtension)
2297 countnoextension -= lstrlenW(lpszFileNameExtension);
2298 *szExtension = '\0';
2300 if (lpszFileExtension)
2302 szExtension[0] = '.';
2303 lstrcpyW(szExtension+1, lpszFileExtension);
2306 for (i = 0; i < 255; i++)
2308 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2310 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2311 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2312 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2313 if (hFile != INVALID_HANDLE_VALUE)
2324 /***********************************************************************
2325 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2327 * The bug we are compensating for is that some drongo at Microsoft
2328 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2329 * As a consequence, CommitUrlCacheEntryA has been effectively
2330 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2331 * is still defined as LPCWSTR. The result (other than madness) is
2332 * that we always need to store lpHeaderInfo in CP_ACP rather than
2333 * in UTF16, and we need to avoid converting lpHeaderInfo in
2334 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2335 * result will lose data for arbitrary binary data.
2338 static BOOL WINAPI CommitUrlCacheEntryInternal(
2339 IN LPCWSTR lpszUrlName,
2340 IN LPCWSTR lpszLocalFileName,
2341 IN FILETIME ExpireTime,
2342 IN FILETIME LastModifiedTime,
2343 IN DWORD CacheEntryType,
2344 IN LPBYTE lpHeaderInfo,
2345 IN DWORD dwHeaderSize,
2346 IN LPCWSTR lpszFileExtension,
2347 IN LPCWSTR lpszOriginalUrl
2350 URLCACHECONTAINER * pContainer;
2351 LPURLCACHE_HEADER pHeader;
2352 struct _HASH_ENTRY * pHashEntry;
2353 CACHEFILE_ENTRY * pEntry;
2354 URL_CACHEFILE_ENTRY * pUrlEntry;
2355 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2356 DWORD dwOffsetLocalFileName = 0;
2357 DWORD dwOffsetHeader = 0;
2358 DWORD dwOffsetFileExtension = 0;
2359 DWORD dwFileSizeLow = 0;
2360 DWORD dwFileSizeHigh = 0;
2361 BYTE cDirectory = 0;
2363 char achFile[MAX_PATH];
2364 LPSTR lpszUrlNameA = NULL;
2365 LPSTR lpszFileExtensionA = NULL;
2366 char *pchLocalFileName = 0;
2369 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2370 debugstr_w(lpszUrlName),
2371 debugstr_w(lpszLocalFileName),
2375 debugstr_w(lpszFileExtension),
2376 debugstr_w(lpszOriginalUrl));
2378 if (lpszOriginalUrl)
2379 WARN(": lpszOriginalUrl ignored\n");
2381 if (lpszLocalFileName)
2385 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2386 if (hFile == INVALID_HANDLE_VALUE)
2388 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2393 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2394 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2396 ERR("couldn't get file size (error is %d)\n", GetLastError());
2404 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2405 if (error != ERROR_SUCCESS)
2407 SetLastError(error);
2411 error = URLCacheContainer_OpenIndex(pContainer);
2412 if (error != ERROR_SUCCESS)
2414 SetLastError(error);
2418 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2421 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2422 lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2425 error = GetLastError();
2428 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2430 if (lpszFileExtension)
2432 len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2433 lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2434 if (!lpszFileExtensionA)
2436 error = GetLastError();
2439 WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2442 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2444 FIXME("entry already in cache - don't know what to do!\n");
2446 * SetLastError(ERROR_FILE_NOT_FOUND);
2452 if (lpszLocalFileName)
2454 BOOL bFound = FALSE;
2456 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2458 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2459 error = ERROR_INVALID_PARAMETER;
2463 /* skip container path prefix */
2464 lpszLocalFileName += lstrlenW(pContainer->path);
2466 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2467 pchLocalFileName = achFile;
2469 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2471 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2480 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2481 error = ERROR_INVALID_PARAMETER;
2485 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2488 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2489 if (lpszLocalFileName)
2491 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2492 dwOffsetLocalFileName = dwBytesNeeded;
2493 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2497 dwOffsetHeader = dwBytesNeeded;
2498 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2500 if (lpszFileExtensionA)
2502 dwOffsetFileExtension = dwBytesNeeded;
2503 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2506 /* round up to next block */
2507 if (dwBytesNeeded % BLOCKSIZE)
2509 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2510 dwBytesNeeded += BLOCKSIZE;
2513 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2515 ERR("no free entries\n");
2516 error = ERROR_DISK_FULL;
2520 /* FindFirstFreeEntry fills in blocks used */
2521 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2522 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2523 pUrlEntry->CacheDir = cDirectory;
2524 pUrlEntry->CacheEntryType = CacheEntryType;
2525 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2526 pUrlEntry->dwExemptDelta = 0;
2527 pUrlEntry->dwHitRate = 0;
2528 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2529 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2530 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2531 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2532 pUrlEntry->dwSizeHigh = 0;
2533 pUrlEntry->dwSizeLow = dwFileSizeLow;
2534 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2535 pUrlEntry->dwUseCount = 0;
2536 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2537 pUrlEntry->LastModifiedTime = LastModifiedTime;
2538 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2539 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2540 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2541 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2544 pUrlEntry->dwUnknown1 = 0;
2545 pUrlEntry->dwUnknown2 = 0;
2546 pUrlEntry->dwUnknown3 = 0x60;
2547 pUrlEntry->Unknown4 = 0;
2548 pUrlEntry->wUnknown5 = 0x1010;
2549 pUrlEntry->dwUnknown7 = 0;
2550 pUrlEntry->dwUnknown8 = 0;
2553 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2554 if (dwOffsetLocalFileName)
2555 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2557 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2558 if (dwOffsetFileExtension)
2559 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2561 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2562 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2563 if (error != ERROR_SUCCESS)
2564 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2567 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2568 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2569 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2571 if (error == ERROR_SUCCESS)
2575 SetLastError(error);
2580 /***********************************************************************
2581 * CommitUrlCacheEntryA (WININET.@)
2584 BOOL WINAPI CommitUrlCacheEntryA(
2585 IN LPCSTR lpszUrlName,
2586 IN LPCSTR lpszLocalFileName,
2587 IN FILETIME ExpireTime,
2588 IN FILETIME LastModifiedTime,
2589 IN DWORD CacheEntryType,
2590 IN LPBYTE lpHeaderInfo,
2591 IN DWORD dwHeaderSize,
2592 IN LPCSTR lpszFileExtension,
2593 IN LPCSTR lpszOriginalUrl
2597 WCHAR *url_name = NULL;
2598 WCHAR *local_file_name = NULL;
2599 WCHAR *original_url = NULL;
2600 WCHAR *file_extension = NULL;
2601 BOOL bSuccess = FALSE;
2603 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2604 debugstr_a(lpszUrlName),
2605 debugstr_a(lpszLocalFileName),
2609 debugstr_a(lpszFileExtension),
2610 debugstr_a(lpszOriginalUrl));
2612 len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2613 url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2616 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2618 if (lpszLocalFileName)
2620 len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2621 local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2622 if (!local_file_name)
2624 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2626 if (lpszFileExtension)
2628 len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2629 file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2630 if (!file_extension)
2632 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2634 if (lpszOriginalUrl)
2636 len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2637 original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2640 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2643 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2644 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2645 file_extension, original_url);
2648 HeapFree(GetProcessHeap(), 0, original_url);
2649 HeapFree(GetProcessHeap(), 0, file_extension);
2650 HeapFree(GetProcessHeap(), 0, local_file_name);
2651 HeapFree(GetProcessHeap(), 0, url_name);
2656 /***********************************************************************
2657 * CommitUrlCacheEntryW (WININET.@)
2660 BOOL WINAPI CommitUrlCacheEntryW(
2661 IN LPCWSTR lpszUrlName,
2662 IN LPCWSTR lpszLocalFileName,
2663 IN FILETIME ExpireTime,
2664 IN FILETIME LastModifiedTime,
2665 IN DWORD CacheEntryType,
2666 IN LPWSTR lpHeaderInfo,
2667 IN DWORD dwHeaderSize,
2668 IN LPCWSTR lpszFileExtension,
2669 IN LPCWSTR lpszOriginalUrl
2673 BOOL bSuccess = FALSE;
2675 CHAR *header_info = NULL;
2677 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2678 debugstr_w(lpszUrlName),
2679 debugstr_w(lpszLocalFileName),
2683 debugstr_w(lpszFileExtension),
2684 debugstr_w(lpszOriginalUrl));
2686 if (!lpHeaderInfo ||
2687 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2688 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2691 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2692 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2693 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2699 dwError = GetLastError();
2703 HeapFree(GetProcessHeap(), 0, header_info);
2705 SetLastError(dwError);
2711 /***********************************************************************
2712 * ReadUrlCacheEntryStream (WININET.@)
2715 BOOL WINAPI ReadUrlCacheEntryStream(
2716 IN HANDLE hUrlCacheStream,
2717 IN DWORD dwLocation,
2718 IN OUT LPVOID lpBuffer,
2719 IN OUT LPDWORD lpdwLen,
2723 /* Get handle to file from 'stream' */
2724 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2726 if (dwReserved != 0)
2728 ERR("dwReserved != 0\n");
2729 SetLastError(ERROR_INVALID_PARAMETER);
2733 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2735 SetLastError(ERROR_INVALID_HANDLE);
2739 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2741 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2744 /***********************************************************************
2745 * RetrieveUrlCacheEntryStreamA (WININET.@)
2748 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2749 IN LPCSTR lpszUrlName,
2750 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2751 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2752 IN BOOL fRandomRead,
2756 /* NOTE: this is not the same as the way that the native
2757 * version allocates 'stream' handles. I did it this way
2758 * as it is much easier and no applications should depend
2759 * on this behaviour. (Native version appears to allocate
2760 * indices into a table)
2762 STREAM_HANDLE * pStream;
2765 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2766 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2768 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2770 lpdwCacheEntryInfoBufferSize,
2776 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2781 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2783 if (hFile == INVALID_HANDLE_VALUE)
2786 /* allocate handle storage space */
2787 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2791 SetLastError(ERROR_OUTOFMEMORY);
2795 pStream->hFile = hFile;
2796 strcpy(pStream->lpszUrl, lpszUrlName);
2797 return (HANDLE)pStream;
2800 /***********************************************************************
2801 * RetrieveUrlCacheEntryStreamW (WININET.@)
2804 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2805 IN LPCWSTR lpszUrlName,
2806 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2807 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2808 IN BOOL fRandomRead,
2812 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2813 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2817 /***********************************************************************
2818 * UnlockUrlCacheEntryStream (WININET.@)
2821 BOOL WINAPI UnlockUrlCacheEntryStream(
2822 IN HANDLE hUrlCacheStream,
2826 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2828 if (dwReserved != 0)
2830 ERR("dwReserved != 0\n");
2831 SetLastError(ERROR_INVALID_PARAMETER);
2835 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2837 SetLastError(ERROR_INVALID_HANDLE);
2841 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2844 /* close file handle */
2845 CloseHandle(pStream->hFile);
2847 /* free allocated space */
2848 HeapFree(GetProcessHeap(), 0, pStream);
2854 /***********************************************************************
2855 * DeleteUrlCacheEntryA (WININET.@)
2858 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2860 URLCACHECONTAINER * pContainer;
2861 LPURLCACHE_HEADER pHeader;
2862 struct _HASH_ENTRY * pHashEntry;
2863 CACHEFILE_ENTRY * pEntry;
2866 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2868 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2869 if (error != ERROR_SUCCESS)
2871 SetLastError(error);
2875 error = URLCacheContainer_OpenIndex(pContainer);
2876 if (error != ERROR_SUCCESS)
2878 SetLastError(error);
2882 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2885 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2887 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2888 TRACE("entry %s not found!\n", lpszUrlName);
2889 SetLastError(ERROR_FILE_NOT_FOUND);
2893 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2894 URLCache_DeleteEntry(pHeader, pEntry);
2896 URLCache_DeleteEntryFromHash(pHashEntry);
2898 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2903 /***********************************************************************
2904 * DeleteUrlCacheEntryW (WININET.@)
2907 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2909 URLCACHECONTAINER * pContainer;
2910 LPURLCACHE_HEADER pHeader;
2911 struct _HASH_ENTRY * pHashEntry;
2912 CACHEFILE_ENTRY * pEntry;
2917 TRACE("(%s)\n", debugstr_w(lpszUrlName));
2919 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2920 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2923 SetLastError(ERROR_OUTOFMEMORY);
2926 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2928 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2929 if (error != ERROR_SUCCESS)
2931 HeapFree(GetProcessHeap(), 0, urlA);
2932 SetLastError(error);
2936 error = URLCacheContainer_OpenIndex(pContainer);
2937 if (error != ERROR_SUCCESS)
2939 HeapFree(GetProcessHeap(), 0, urlA);
2940 SetLastError(error);
2944 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2946 HeapFree(GetProcessHeap(), 0, urlA);
2950 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2952 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2953 TRACE("entry %s not found!\n", debugstr_a(urlA));
2954 HeapFree(GetProcessHeap(), 0, urlA);
2955 SetLastError(ERROR_FILE_NOT_FOUND);
2959 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2960 URLCache_DeleteEntry(pHeader, pEntry);
2962 URLCache_DeleteEntryFromHash(pHashEntry);
2964 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2966 HeapFree(GetProcessHeap(), 0, urlA);
2970 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2972 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2976 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2978 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2982 /***********************************************************************
2983 * CreateCacheContainerA (WININET.@)
2985 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2986 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2988 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2989 d1, d2, d3, d4, d5, d6, d7, d8);
2993 /***********************************************************************
2994 * CreateCacheContainerW (WININET.@)
2996 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2997 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2999 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3000 d1, d2, d3, d4, d5, d6, d7, d8);
3004 /***********************************************************************
3005 * FindFirstUrlCacheContainerA (WININET.@)
3007 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3009 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3013 /***********************************************************************
3014 * FindFirstUrlCacheContainerW (WININET.@)
3016 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3018 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3022 /***********************************************************************
3023 * FindNextUrlCacheContainerA (WININET.@)
3025 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3027 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3031 /***********************************************************************
3032 * FindNextUrlCacheContainerW (WININET.@)
3034 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3036 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3040 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3041 LPCSTR lpszUrlSearchPattern,
3045 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3046 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3048 LPDWORD pcbReserved2,
3052 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3053 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3054 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3055 SetLastError(ERROR_FILE_NOT_FOUND);
3059 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3060 LPCWSTR lpszUrlSearchPattern,
3064 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3065 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3067 LPDWORD pcbReserved2,
3071 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3072 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3073 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3074 SetLastError(ERROR_FILE_NOT_FOUND);
3078 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3080 typedef struct URLCacheFindEntryHandle
3083 LPWSTR lpszUrlSearchPattern;
3084 DWORD dwContainerIndex;
3085 DWORD dwHashTableIndex;
3086 DWORD dwHashEntryIndex;
3087 } URLCacheFindEntryHandle;
3089 /***********************************************************************
3090 * FindFirstUrlCacheEntryA (WININET.@)
3093 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3094 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3096 URLCacheFindEntryHandle *pEntryHandle;
3098 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3100 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3104 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3105 if (lpszUrlSearchPattern)
3107 int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
3108 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3109 if (!pEntryHandle->lpszUrlSearchPattern)
3111 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3114 MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
3117 pEntryHandle->lpszUrlSearchPattern = NULL;
3118 pEntryHandle->dwContainerIndex = 0;
3119 pEntryHandle->dwHashTableIndex = 0;
3120 pEntryHandle->dwHashEntryIndex = 0;
3122 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3124 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3127 return pEntryHandle;
3130 /***********************************************************************
3131 * FindFirstUrlCacheEntryW (WININET.@)
3134 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3135 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3137 URLCacheFindEntryHandle *pEntryHandle;
3139 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3141 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3145 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3146 if (lpszUrlSearchPattern)
3148 int len = strlenW(lpszUrlSearchPattern);
3149 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3150 if (!pEntryHandle->lpszUrlSearchPattern)
3152 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3155 memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3158 pEntryHandle->lpszUrlSearchPattern = NULL;
3159 pEntryHandle->dwContainerIndex = 0;
3160 pEntryHandle->dwHashTableIndex = 0;
3161 pEntryHandle->dwHashEntryIndex = 0;
3163 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3165 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3168 return pEntryHandle;
3171 /***********************************************************************
3172 * FindNextUrlCacheEntryA (WININET.@)
3174 BOOL WINAPI FindNextUrlCacheEntryA(
3176 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3177 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3179 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3180 URLCACHECONTAINER * pContainer;
3182 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3184 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3186 SetLastError(ERROR_INVALID_HANDLE);
3190 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3191 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3193 LPURLCACHE_HEADER pHeader;
3194 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3197 error = URLCacheContainer_OpenIndex(pContainer);
3198 if (error != ERROR_SUCCESS)
3200 SetLastError(error);
3204 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3207 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3208 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3210 const struct _HASH_ENTRY *pHashEntry = NULL;
3211 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3212 pEntryHandle->dwHashEntryIndex++)
3214 const URL_CACHEFILE_ENTRY *pUrlEntry;
3215 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3217 if (pEntry->dwSignature != URL_SIGNATURE)
3220 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3221 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3222 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3224 error = URLCache_CopyEntry(
3227 lpNextCacheEntryInfo,
3228 lpdwNextCacheEntryInfoBufferSize,
3230 FALSE /* not UNICODE */);
3231 if (error != ERROR_SUCCESS)
3233 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3234 SetLastError(error);
3237 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3239 /* increment the current index so that next time the function
3240 * is called the next entry is returned */
3241 pEntryHandle->dwHashEntryIndex++;
3242 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3247 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3250 SetLastError(ERROR_NO_MORE_ITEMS);
3254 /***********************************************************************
3255 * FindNextUrlCacheEntryW (WININET.@)
3257 BOOL WINAPI FindNextUrlCacheEntryW(
3259 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3260 LPDWORD lpdwNextCacheEntryInfoBufferSize
3263 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3264 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3268 /***********************************************************************
3269 * FindCloseUrlCache (WININET.@)
3271 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3273 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3275 TRACE("(%p)\n", hEnumHandle);
3277 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3279 SetLastError(ERROR_INVALID_HANDLE);
3283 pEntryHandle->dwMagic = 0;
3284 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3285 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3290 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3291 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3293 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3294 dwSearchCondition, lpGroupId, lpReserved);
3298 BOOL WINAPI FindNextUrlCacheEntryExA(
3300 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3301 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3303 LPDWORD pcbReserved2,
3307 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3308 lpReserved, pcbReserved2, lpReserved3);
3312 BOOL WINAPI FindNextUrlCacheEntryExW(
3314 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3315 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3317 LPDWORD pcbReserved2,
3321 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3322 lpReserved, pcbReserved2, lpReserved3);
3326 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3328 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3332 /***********************************************************************
3333 * CreateUrlCacheGroup (WININET.@)
3336 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3338 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3342 /***********************************************************************
3343 * DeleteUrlCacheGroup (WININET.@)
3346 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3348 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3349 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3353 /***********************************************************************
3354 * SetUrlCacheEntryGroupA (WININET.@)
3357 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3358 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3361 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3362 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3363 pbGroupAttributes, cbGroupAttributes, lpReserved);
3364 SetLastError(ERROR_FILE_NOT_FOUND);
3368 /***********************************************************************
3369 * SetUrlCacheEntryGroupW (WININET.@)
3372 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3373 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3376 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3377 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3378 pbGroupAttributes, cbGroupAttributes, lpReserved);
3379 SetLastError(ERROR_FILE_NOT_FOUND);
3383 /***********************************************************************
3384 * GetUrlCacheConfigInfoW (WININET.@)
3386 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3388 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3389 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3393 /***********************************************************************
3394 * GetUrlCacheConfigInfoA (WININET.@)
3396 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3398 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3400 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3401 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3405 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3406 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3407 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3409 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3410 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3411 lpdwGroupInfo, lpReserved);
3415 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3416 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3417 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3419 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3420 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3421 lpdwGroupInfo, lpReserved);
3425 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3426 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3428 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3429 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3433 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3434 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3436 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3437 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3441 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3443 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3447 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3449 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3453 /***********************************************************************
3454 * DeleteIE3Cache (WININET.@)
3456 * Deletes the files used by the IE3 URL caching system.
3459 * hWnd [I] A dummy window.
3460 * hInst [I] Instance of process calling the function.
3461 * lpszCmdLine [I] Options used by function.
3462 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3464 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3466 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3470 /***********************************************************************
3471 * IsUrlCacheEntryExpiredA (WININET.@)
3475 * dwFlags [I] Unknown
3476 * pftLastModified [O] Last modified time
3478 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3480 LPURLCACHE_HEADER pHeader;
3481 struct _HASH_ENTRY * pHashEntry;
3482 const CACHEFILE_ENTRY * pEntry;
3483 const URL_CACHEFILE_ENTRY * pUrlEntry;
3484 URLCACHECONTAINER * pContainer;
3487 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3489 error = URLCacheContainers_FindContainerA(url, &pContainer);
3490 if (error != ERROR_SUCCESS)
3492 SetLastError(error);
3496 error = URLCacheContainer_OpenIndex(pContainer);
3497 if (error != ERROR_SUCCESS)
3499 SetLastError(error);
3503 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3506 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3508 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3509 TRACE("entry %s not found!\n", url);
3510 SetLastError(ERROR_FILE_NOT_FOUND);
3514 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3515 if (pEntry->dwSignature != URL_SIGNATURE)
3517 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3518 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3519 SetLastError(ERROR_FILE_NOT_FOUND);
3523 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3525 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3527 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3532 /***********************************************************************
3533 * IsUrlCacheEntryExpiredW (WININET.@)
3537 * dwFlags [I] Unknown
3538 * pftLastModified [O] Last modified time
3540 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3542 LPURLCACHE_HEADER pHeader;
3543 struct _HASH_ENTRY * pHashEntry;
3544 const CACHEFILE_ENTRY * pEntry;
3545 const URL_CACHEFILE_ENTRY * pUrlEntry;
3546 URLCACHECONTAINER * pContainer;
3549 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3551 error = URLCacheContainers_FindContainerW(url, &pContainer);
3552 if (error != ERROR_SUCCESS)
3554 SetLastError(error);
3558 error = URLCacheContainer_OpenIndex(pContainer);
3559 if (error != ERROR_SUCCESS)
3561 SetLastError(error);
3565 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3568 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3570 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3571 TRACE("entry %s not found!\n", debugstr_w(url));
3572 SetLastError(ERROR_FILE_NOT_FOUND);
3576 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3577 if (pEntry->dwSignature != URL_SIGNATURE)
3579 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3580 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3581 SetLastError(ERROR_FILE_NOT_FOUND);
3585 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3587 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3589 URLCacheContainer_UnlockIndex(pContainer, pHeader);