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 prefx %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 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(lpCacheEntryInfo->lpszLocalFileName));
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_w(lpCacheEntryInfo->lpszLocalFileName));
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 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1821 if (error != ERROR_SUCCESS)
1823 SetLastError(error);
1827 error = URLCacheContainer_OpenIndex(pContainer);
1828 if (error != ERROR_SUCCESS)
1830 SetLastError(error);
1834 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1837 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1839 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1840 TRACE("entry %s not found!\n", lpszUrlName);
1841 SetLastError(ERROR_FILE_NOT_FOUND);
1845 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1846 if (pEntry->dwSignature != URL_SIGNATURE)
1848 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1849 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1850 SetLastError(ERROR_FILE_NOT_FOUND);
1854 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1855 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1856 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1858 pUrlEntry->dwHitRate++;
1859 pUrlEntry->dwUseCount++;
1860 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1862 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
1863 lpdwCacheEntryInfoBufferSize, pUrlEntry,
1865 if (error != ERROR_SUCCESS)
1867 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1868 SetLastError(error);
1871 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1873 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1878 /***********************************************************************
1879 * RetrieveUrlCacheEntryFileW (WININET.@)
1882 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1883 IN LPCWSTR lpszUrlName,
1884 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1885 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1889 LPURLCACHE_HEADER pHeader;
1890 struct _HASH_ENTRY * pHashEntry;
1891 CACHEFILE_ENTRY * pEntry;
1892 URL_CACHEFILE_ENTRY * pUrlEntry;
1893 URLCACHECONTAINER * pContainer;
1896 TRACE("(%s, %p, %p, 0x%08x)\n",
1897 debugstr_w(lpszUrlName),
1899 lpdwCacheEntryInfoBufferSize,
1902 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
1903 if (error != ERROR_SUCCESS)
1905 SetLastError(error);
1909 error = URLCacheContainer_OpenIndex(pContainer);
1910 if (error != ERROR_SUCCESS)
1912 SetLastError(error);
1916 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1919 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1921 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1922 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1923 SetLastError(ERROR_FILE_NOT_FOUND);
1927 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1928 if (pEntry->dwSignature != URL_SIGNATURE)
1930 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1932 SetLastError(ERROR_FILE_NOT_FOUND);
1936 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1937 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1938 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1940 pUrlEntry->dwHitRate++;
1941 pUrlEntry->dwUseCount++;
1942 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1944 error = URLCache_CopyEntry(
1947 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1948 lpdwCacheEntryInfoBufferSize,
1950 TRUE /* UNICODE */);
1951 if (error != ERROR_SUCCESS)
1953 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1954 SetLastError(error);
1957 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1959 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1964 /***********************************************************************
1965 * UnlockUrlCacheEntryFileA (WININET.@)
1968 BOOL WINAPI UnlockUrlCacheEntryFileA(
1969 IN LPCSTR lpszUrlName,
1973 LPURLCACHE_HEADER pHeader;
1974 struct _HASH_ENTRY * pHashEntry;
1975 CACHEFILE_ENTRY * pEntry;
1976 URL_CACHEFILE_ENTRY * pUrlEntry;
1977 URLCACHECONTAINER * pContainer;
1980 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1984 ERR("dwReserved != 0\n");
1985 SetLastError(ERROR_INVALID_PARAMETER);
1989 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1990 if (error != ERROR_SUCCESS)
1992 SetLastError(error);
1996 error = URLCacheContainer_OpenIndex(pContainer);
1997 if (error != ERROR_SUCCESS)
1999 SetLastError(error);
2003 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2006 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2008 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2009 TRACE("entry %s not found!\n", lpszUrlName);
2010 SetLastError(ERROR_FILE_NOT_FOUND);
2014 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2015 if (pEntry->dwSignature != URL_SIGNATURE)
2017 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2018 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2019 SetLastError(ERROR_FILE_NOT_FOUND);
2023 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2025 if (pUrlEntry->dwUseCount == 0)
2027 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2030 pUrlEntry->dwUseCount--;
2031 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2033 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2038 /***********************************************************************
2039 * UnlockUrlCacheEntryFileW (WININET.@)
2042 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2044 LPURLCACHE_HEADER pHeader;
2045 struct _HASH_ENTRY * pHashEntry;
2046 CACHEFILE_ENTRY * pEntry;
2047 URL_CACHEFILE_ENTRY * pUrlEntry;
2048 URLCACHECONTAINER * pContainer;
2051 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2055 ERR("dwReserved != 0\n");
2056 SetLastError(ERROR_INVALID_PARAMETER);
2060 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2061 if (error != ERROR_SUCCESS)
2063 SetLastError(error);
2067 error = URLCacheContainer_OpenIndex(pContainer);
2068 if (error != ERROR_SUCCESS)
2070 SetLastError(error);
2074 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2077 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2079 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2080 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2081 SetLastError(ERROR_FILE_NOT_FOUND);
2085 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2086 if (pEntry->dwSignature != URL_SIGNATURE)
2088 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2089 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2090 SetLastError(ERROR_FILE_NOT_FOUND);
2094 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2096 if (pUrlEntry->dwUseCount == 0)
2098 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2101 pUrlEntry->dwUseCount--;
2102 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2104 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2109 /***********************************************************************
2110 * CreateUrlCacheEntryA (WININET.@)
2113 BOOL WINAPI CreateUrlCacheEntryA(
2114 IN LPCSTR lpszUrlName,
2115 IN DWORD dwExpectedFileSize,
2116 IN LPCSTR lpszFileExtension,
2117 OUT LPSTR lpszFileName,
2123 WCHAR *file_extension;
2124 WCHAR file_name[MAX_PATH];
2125 BOOL bSuccess = FALSE;
2128 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2129 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2131 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2132 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2133 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2135 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2136 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2138 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2144 dwError = GetLastError();
2149 dwError = GetLastError();
2151 HeapFree(GetProcessHeap(), 0, file_extension);
2155 dwError = GetLastError();
2157 HeapFree(GetProcessHeap(), 0, url_name);
2159 SetLastError(dwError);
2163 /***********************************************************************
2164 * CreateUrlCacheEntryW (WININET.@)
2167 BOOL WINAPI CreateUrlCacheEntryW(
2168 IN LPCWSTR lpszUrlName,
2169 IN DWORD dwExpectedFileSize,
2170 IN LPCWSTR lpszFileExtension,
2171 OUT LPWSTR lpszFileName,
2175 URLCACHECONTAINER * pContainer;
2176 LPURLCACHE_HEADER pHeader;
2177 CHAR szFile[MAX_PATH];
2178 WCHAR szExtension[MAX_PATH];
2179 LPCWSTR lpszUrlPart;
2181 LPCWSTR lpszFileNameExtension;
2182 LPWSTR lpszFileNameNoPath;
2184 int countnoextension;
2187 BOOL bFound = FALSE;
2190 static const WCHAR szWWW[] = {'w','w','w',0};
2192 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2193 debugstr_w(lpszUrlName),
2195 debugstr_w(lpszFileExtension),
2201 ERR("dwReserved != 0\n");
2202 SetLastError(ERROR_INVALID_PARAMETER);
2206 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2208 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2211 for (lpszUrlPart = lpszUrlEnd;
2212 (lpszUrlPart >= lpszUrlName);
2215 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2221 else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2223 lpszUrlEnd = lpszUrlPart;
2226 if (!lstrcmpW(lpszUrlPart, szWWW))
2228 lpszUrlPart += lstrlenW(szWWW);
2231 count = lpszUrlEnd - lpszUrlPart;
2233 if (bFound && (count < MAX_PATH))
2235 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2239 /* FIXME: get rid of illegal characters like \, / and : */
2243 FIXME("need to generate a random filename\n");
2246 TRACE("File name: %s\n", debugstr_a(szFile));
2248 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2249 if (error != ERROR_SUCCESS)
2251 SetLastError(error);
2255 error = URLCacheContainer_OpenIndex(pContainer);
2256 if (error != ERROR_SUCCESS)
2258 SetLastError(error);
2262 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2265 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2267 lBufferSize = MAX_PATH * sizeof(WCHAR);
2268 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2270 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2272 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2273 lpszFileNameNoPath >= lpszFileName;
2274 --lpszFileNameNoPath)
2276 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2280 countnoextension = lstrlenW(lpszFileNameNoPath);
2281 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2282 if (lpszFileNameExtension)
2283 countnoextension -= lstrlenW(lpszFileNameExtension);
2284 *szExtension = '\0';
2286 if (lpszFileExtension)
2288 szExtension[0] = '.';
2289 lstrcpyW(szExtension+1, lpszFileExtension);
2292 for (i = 0; i < 255; i++)
2294 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2296 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2297 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2298 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2299 if (hFile != INVALID_HANDLE_VALUE)
2310 /***********************************************************************
2311 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2313 * The bug we are compensating for is that some drongo at Microsoft
2314 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2315 * As a consequence, CommitUrlCacheEntryA has been effectively
2316 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2317 * is still defined as LPCWSTR. The result (other than madness) is
2318 * that we always need to store lpHeaderInfo in CP_ACP rather than
2319 * in UTF16, and we need to avoid converting lpHeaderInfo in
2320 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2321 * result will lose data for arbitrary binary data.
2324 static BOOL WINAPI CommitUrlCacheEntryInternal(
2325 IN LPCWSTR lpszUrlName,
2326 IN LPCWSTR lpszLocalFileName,
2327 IN FILETIME ExpireTime,
2328 IN FILETIME LastModifiedTime,
2329 IN DWORD CacheEntryType,
2330 IN LPBYTE lpHeaderInfo,
2331 IN DWORD dwHeaderSize,
2332 IN LPCWSTR lpszFileExtension,
2333 IN LPCWSTR lpszOriginalUrl
2336 URLCACHECONTAINER * pContainer;
2337 LPURLCACHE_HEADER pHeader;
2338 struct _HASH_ENTRY * pHashEntry;
2339 CACHEFILE_ENTRY * pEntry;
2340 URL_CACHEFILE_ENTRY * pUrlEntry;
2341 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2342 DWORD dwOffsetLocalFileName = 0;
2343 DWORD dwOffsetHeader = 0;
2344 DWORD dwOffsetFileExtension = 0;
2345 DWORD dwFileSizeLow = 0;
2346 DWORD dwFileSizeHigh = 0;
2347 BYTE cDirectory = 0;
2349 char achFile[MAX_PATH];
2350 LPSTR lpszUrlNameA = NULL;
2351 LPSTR lpszFileExtensionA = NULL;
2352 char *pchLocalFileName = 0;
2355 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2356 debugstr_w(lpszUrlName),
2357 debugstr_w(lpszLocalFileName),
2361 debugstr_w(lpszFileExtension),
2362 debugstr_w(lpszOriginalUrl));
2364 if (lpszOriginalUrl)
2365 WARN(": lpszOriginalUrl ignored\n");
2367 if (lpszLocalFileName)
2371 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2372 if (hFile == INVALID_HANDLE_VALUE)
2374 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2379 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2380 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2382 ERR("couldn't get file size (error is %d)\n", GetLastError());
2390 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2391 if (error != ERROR_SUCCESS)
2393 SetLastError(error);
2397 error = URLCacheContainer_OpenIndex(pContainer);
2398 if (error != ERROR_SUCCESS)
2400 SetLastError(error);
2404 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2407 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2408 lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2411 error = GetLastError();
2414 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2416 if (lpszFileExtension)
2418 len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2419 lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2420 if (!lpszFileExtensionA)
2422 error = GetLastError();
2425 WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2428 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2430 FIXME("entry already in cache - don't know what to do!\n");
2432 * SetLastError(ERROR_FILE_NOT_FOUND);
2438 if (lpszLocalFileName)
2440 BOOL bFound = FALSE;
2442 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2444 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2445 error = ERROR_INVALID_PARAMETER;
2449 /* skip container path prefix */
2450 lpszLocalFileName += lstrlenW(pContainer->path);
2452 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2453 pchLocalFileName = achFile;
2455 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2457 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2466 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2467 error = ERROR_INVALID_PARAMETER;
2471 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2474 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2475 if (lpszLocalFileName)
2477 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2478 dwOffsetLocalFileName = dwBytesNeeded;
2479 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2483 dwOffsetHeader = dwBytesNeeded;
2484 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2486 if (lpszFileExtensionA)
2488 dwOffsetFileExtension = dwBytesNeeded;
2489 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2492 /* round up to next block */
2493 if (dwBytesNeeded % BLOCKSIZE)
2495 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2496 dwBytesNeeded += BLOCKSIZE;
2499 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2501 ERR("no free entries\n");
2502 error = ERROR_DISK_FULL;
2506 /* FindFirstFreeEntry fills in blocks used */
2507 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2508 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2509 pUrlEntry->CacheDir = cDirectory;
2510 pUrlEntry->CacheEntryType = CacheEntryType;
2511 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2512 pUrlEntry->dwExemptDelta = 0;
2513 pUrlEntry->dwHitRate = 0;
2514 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2515 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2516 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2517 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2518 pUrlEntry->dwSizeHigh = 0;
2519 pUrlEntry->dwSizeLow = dwFileSizeLow;
2520 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2521 pUrlEntry->dwUseCount = 0;
2522 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2523 pUrlEntry->LastModifiedTime = LastModifiedTime;
2524 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2525 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2526 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2527 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2530 pUrlEntry->dwUnknown1 = 0;
2531 pUrlEntry->dwUnknown2 = 0;
2532 pUrlEntry->dwUnknown3 = 0x60;
2533 pUrlEntry->Unknown4 = 0;
2534 pUrlEntry->wUnknown5 = 0x1010;
2535 pUrlEntry->dwUnknown7 = 0;
2536 pUrlEntry->dwUnknown8 = 0;
2539 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2540 if (dwOffsetLocalFileName)
2541 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2543 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2544 if (dwOffsetFileExtension)
2545 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2547 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2548 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2549 if (error != ERROR_SUCCESS)
2550 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2553 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2554 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2555 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2557 if (error == ERROR_SUCCESS)
2561 SetLastError(error);
2566 /***********************************************************************
2567 * CommitUrlCacheEntryA (WININET.@)
2570 BOOL WINAPI CommitUrlCacheEntryA(
2571 IN LPCSTR lpszUrlName,
2572 IN LPCSTR lpszLocalFileName,
2573 IN FILETIME ExpireTime,
2574 IN FILETIME LastModifiedTime,
2575 IN DWORD CacheEntryType,
2576 IN LPBYTE lpHeaderInfo,
2577 IN DWORD dwHeaderSize,
2578 IN LPCSTR lpszFileExtension,
2579 IN LPCSTR lpszOriginalUrl
2583 WCHAR *url_name = NULL;
2584 WCHAR *local_file_name = NULL;
2585 WCHAR *original_url = NULL;
2586 WCHAR *file_extension = NULL;
2587 BOOL bSuccess = FALSE;
2589 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2590 debugstr_a(lpszUrlName),
2591 debugstr_a(lpszLocalFileName),
2595 debugstr_a(lpszFileExtension),
2596 debugstr_a(lpszOriginalUrl));
2598 len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2599 url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2602 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2604 if (lpszLocalFileName)
2606 len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2607 local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2608 if (!local_file_name)
2610 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2612 if (lpszFileExtension)
2614 len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2615 file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2616 if (!file_extension)
2618 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2620 if (lpszOriginalUrl)
2622 len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2623 original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2626 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2629 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2630 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2631 file_extension, original_url);
2634 HeapFree(GetProcessHeap(), 0, original_url);
2635 HeapFree(GetProcessHeap(), 0, file_extension);
2636 HeapFree(GetProcessHeap(), 0, local_file_name);
2637 HeapFree(GetProcessHeap(), 0, url_name);
2642 /***********************************************************************
2643 * CommitUrlCacheEntryW (WININET.@)
2646 BOOL WINAPI CommitUrlCacheEntryW(
2647 IN LPCWSTR lpszUrlName,
2648 IN LPCWSTR lpszLocalFileName,
2649 IN FILETIME ExpireTime,
2650 IN FILETIME LastModifiedTime,
2651 IN DWORD CacheEntryType,
2652 IN LPWSTR lpHeaderInfo,
2653 IN DWORD dwHeaderSize,
2654 IN LPCWSTR lpszFileExtension,
2655 IN LPCWSTR lpszOriginalUrl
2659 BOOL bSuccess = FALSE;
2661 CHAR *header_info = NULL;
2663 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2664 debugstr_w(lpszUrlName),
2665 debugstr_w(lpszLocalFileName),
2669 debugstr_w(lpszFileExtension),
2670 debugstr_w(lpszOriginalUrl));
2672 if (!lpHeaderInfo ||
2673 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2674 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2677 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2678 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2679 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2685 dwError = GetLastError();
2689 HeapFree(GetProcessHeap(), 0, header_info);
2691 SetLastError(dwError);
2697 /***********************************************************************
2698 * ReadUrlCacheEntryStream (WININET.@)
2701 BOOL WINAPI ReadUrlCacheEntryStream(
2702 IN HANDLE hUrlCacheStream,
2703 IN DWORD dwLocation,
2704 IN OUT LPVOID lpBuffer,
2705 IN OUT LPDWORD lpdwLen,
2709 /* Get handle to file from 'stream' */
2710 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2712 if (dwReserved != 0)
2714 ERR("dwReserved != 0\n");
2715 SetLastError(ERROR_INVALID_PARAMETER);
2719 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2721 SetLastError(ERROR_INVALID_HANDLE);
2725 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2727 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2730 /***********************************************************************
2731 * RetrieveUrlCacheEntryStreamA (WININET.@)
2734 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2735 IN LPCSTR lpszUrlName,
2736 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2737 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2738 IN BOOL fRandomRead,
2742 /* NOTE: this is not the same as the way that the native
2743 * version allocates 'stream' handles. I did it this way
2744 * as it is much easier and no applications should depend
2745 * on this behaviour. (Native version appears to allocate
2746 * indices into a table)
2748 STREAM_HANDLE * pStream;
2751 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2752 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2754 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2756 lpdwCacheEntryInfoBufferSize,
2762 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2767 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2769 if (hFile == INVALID_HANDLE_VALUE)
2772 /* allocate handle storage space */
2773 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2777 SetLastError(ERROR_OUTOFMEMORY);
2781 pStream->hFile = hFile;
2782 strcpy(pStream->lpszUrl, lpszUrlName);
2783 return (HANDLE)pStream;
2786 /***********************************************************************
2787 * RetrieveUrlCacheEntryStreamW (WININET.@)
2790 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2791 IN LPCWSTR lpszUrlName,
2792 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2793 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2794 IN BOOL fRandomRead,
2798 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2799 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2803 /***********************************************************************
2804 * UnlockUrlCacheEntryStream (WININET.@)
2807 BOOL WINAPI UnlockUrlCacheEntryStream(
2808 IN HANDLE hUrlCacheStream,
2812 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2814 if (dwReserved != 0)
2816 ERR("dwReserved != 0\n");
2817 SetLastError(ERROR_INVALID_PARAMETER);
2821 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2823 SetLastError(ERROR_INVALID_HANDLE);
2827 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2830 /* close file handle */
2831 CloseHandle(pStream->hFile);
2833 /* free allocated space */
2834 HeapFree(GetProcessHeap(), 0, pStream);
2840 /***********************************************************************
2841 * DeleteUrlCacheEntryA (WININET.@)
2844 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2846 URLCACHECONTAINER * pContainer;
2847 LPURLCACHE_HEADER pHeader;
2848 struct _HASH_ENTRY * pHashEntry;
2849 CACHEFILE_ENTRY * pEntry;
2852 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2854 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2855 if (error != ERROR_SUCCESS)
2857 SetLastError(error);
2861 error = URLCacheContainer_OpenIndex(pContainer);
2862 if (error != ERROR_SUCCESS)
2864 SetLastError(error);
2868 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2871 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2873 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2874 TRACE("entry %s not found!\n", lpszUrlName);
2875 SetLastError(ERROR_FILE_NOT_FOUND);
2879 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2880 URLCache_DeleteEntry(pHeader, pEntry);
2882 URLCache_DeleteEntryFromHash(pHashEntry);
2884 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2889 /***********************************************************************
2890 * DeleteUrlCacheEntryW (WININET.@)
2893 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2895 URLCACHECONTAINER * pContainer;
2896 LPURLCACHE_HEADER pHeader;
2897 struct _HASH_ENTRY * pHashEntry;
2898 CACHEFILE_ENTRY * pEntry;
2903 TRACE("(%s)\n", debugstr_w(lpszUrlName));
2905 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2906 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2909 SetLastError(ERROR_OUTOFMEMORY);
2912 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2914 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2915 if (error != ERROR_SUCCESS)
2917 HeapFree(GetProcessHeap(), 0, urlA);
2918 SetLastError(error);
2922 error = URLCacheContainer_OpenIndex(pContainer);
2923 if (error != ERROR_SUCCESS)
2925 HeapFree(GetProcessHeap(), 0, urlA);
2926 SetLastError(error);
2930 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2932 HeapFree(GetProcessHeap(), 0, urlA);
2936 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2938 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2939 TRACE("entry %s not found!\n", debugstr_a(urlA));
2940 HeapFree(GetProcessHeap(), 0, urlA);
2941 SetLastError(ERROR_FILE_NOT_FOUND);
2945 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2946 URLCache_DeleteEntry(pHeader, pEntry);
2948 URLCache_DeleteEntryFromHash(pHashEntry);
2950 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2952 HeapFree(GetProcessHeap(), 0, urlA);
2956 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2958 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2962 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2964 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2968 /***********************************************************************
2969 * CreateCacheContainerA (WININET.@)
2971 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2972 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2974 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2975 d1, d2, d3, d4, d5, d6, d7, d8);
2979 /***********************************************************************
2980 * CreateCacheContainerW (WININET.@)
2982 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2983 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2985 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2986 d1, d2, d3, d4, d5, d6, d7, d8);
2990 /***********************************************************************
2991 * FindFirstUrlCacheContainerA (WININET.@)
2993 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2995 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2999 /***********************************************************************
3000 * FindFirstUrlCacheContainerW (WININET.@)
3002 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3004 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3008 /***********************************************************************
3009 * FindNextUrlCacheContainerA (WININET.@)
3011 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3013 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3017 /***********************************************************************
3018 * FindNextUrlCacheContainerW (WININET.@)
3020 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3022 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3026 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3027 LPCSTR lpszUrlSearchPattern,
3031 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3032 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3034 LPDWORD pcbReserved2,
3038 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3039 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3040 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3041 SetLastError(ERROR_FILE_NOT_FOUND);
3045 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3046 LPCWSTR lpszUrlSearchPattern,
3050 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3051 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3053 LPDWORD pcbReserved2,
3057 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3058 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3059 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3060 SetLastError(ERROR_FILE_NOT_FOUND);
3064 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3066 typedef struct URLCacheFindEntryHandle
3069 LPWSTR lpszUrlSearchPattern;
3070 DWORD dwContainerIndex;
3071 DWORD dwHashTableIndex;
3072 DWORD dwHashEntryIndex;
3073 } URLCacheFindEntryHandle;
3075 /***********************************************************************
3076 * FindFirstUrlCacheEntryA (WININET.@)
3079 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3080 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3082 URLCacheFindEntryHandle *pEntryHandle;
3084 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3086 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3090 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3091 if (lpszUrlSearchPattern)
3093 int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
3094 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3095 if (!pEntryHandle->lpszUrlSearchPattern)
3097 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3100 MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
3103 pEntryHandle->lpszUrlSearchPattern = NULL;
3104 pEntryHandle->dwContainerIndex = 0;
3105 pEntryHandle->dwHashTableIndex = 0;
3106 pEntryHandle->dwHashEntryIndex = 0;
3108 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3110 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3113 return pEntryHandle;
3116 /***********************************************************************
3117 * FindFirstUrlCacheEntryW (WININET.@)
3120 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3121 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3123 URLCacheFindEntryHandle *pEntryHandle;
3125 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3127 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3131 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3132 if (lpszUrlSearchPattern)
3134 int len = strlenW(lpszUrlSearchPattern);
3135 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3136 if (!pEntryHandle->lpszUrlSearchPattern)
3138 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3141 memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3144 pEntryHandle->lpszUrlSearchPattern = NULL;
3145 pEntryHandle->dwContainerIndex = 0;
3146 pEntryHandle->dwHashTableIndex = 0;
3147 pEntryHandle->dwHashEntryIndex = 0;
3149 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3151 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3154 return pEntryHandle;
3157 /***********************************************************************
3158 * FindNextUrlCacheEntryA (WININET.@)
3160 BOOL WINAPI FindNextUrlCacheEntryA(
3162 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3163 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3165 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3166 URLCACHECONTAINER * pContainer;
3168 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3170 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3172 SetLastError(ERROR_INVALID_HANDLE);
3176 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3177 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3179 LPURLCACHE_HEADER pHeader;
3180 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3183 error = URLCacheContainer_OpenIndex(pContainer);
3184 if (error != ERROR_SUCCESS)
3186 SetLastError(error);
3190 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3193 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3194 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3196 const struct _HASH_ENTRY *pHashEntry = NULL;
3197 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3198 pEntryHandle->dwHashEntryIndex++)
3200 const URL_CACHEFILE_ENTRY *pUrlEntry;
3201 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3203 if (pEntry->dwSignature != URL_SIGNATURE)
3206 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3207 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3208 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3210 error = URLCache_CopyEntry(
3213 lpNextCacheEntryInfo,
3214 lpdwNextCacheEntryInfoBufferSize,
3216 FALSE /* not UNICODE */);
3217 if (error != ERROR_SUCCESS)
3219 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3220 SetLastError(error);
3223 TRACE("Local File Name: %s\n", debugstr_a(lpNextCacheEntryInfo->lpszLocalFileName));
3225 /* increment the current index so that next time the function
3226 * is called the next entry is returned */
3227 pEntryHandle->dwHashEntryIndex++;
3228 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3233 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3236 SetLastError(ERROR_NO_MORE_ITEMS);
3240 /***********************************************************************
3241 * FindNextUrlCacheEntryW (WININET.@)
3243 BOOL WINAPI FindNextUrlCacheEntryW(
3245 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3246 LPDWORD lpdwNextCacheEntryInfoBufferSize
3249 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3250 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3254 /***********************************************************************
3255 * FindCloseUrlCache (WININET.@)
3257 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3259 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3261 TRACE("(%p)\n", hEnumHandle);
3263 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3265 SetLastError(ERROR_INVALID_HANDLE);
3269 pEntryHandle->dwMagic = 0;
3270 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3271 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3276 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3277 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3279 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3280 dwSearchCondition, lpGroupId, lpReserved);
3284 BOOL WINAPI FindNextUrlCacheEntryExA(
3286 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3287 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3289 LPDWORD pcbReserved2,
3293 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3294 lpReserved, pcbReserved2, lpReserved3);
3298 BOOL WINAPI FindNextUrlCacheEntryExW(
3300 LPINTERNET_CACHE_ENTRY_INFOW 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 FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3314 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3318 /***********************************************************************
3319 * CreateUrlCacheGroup (WININET.@)
3322 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3324 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3328 /***********************************************************************
3329 * DeleteUrlCacheGroup (WININET.@)
3332 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3334 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3335 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3339 /***********************************************************************
3340 * SetUrlCacheEntryGroupA (WININET.@)
3343 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3344 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3347 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3348 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3349 pbGroupAttributes, cbGroupAttributes, lpReserved);
3350 SetLastError(ERROR_FILE_NOT_FOUND);
3354 /***********************************************************************
3355 * SetUrlCacheEntryGroupW (WININET.@)
3358 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3359 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3362 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3363 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3364 pbGroupAttributes, cbGroupAttributes, lpReserved);
3365 SetLastError(ERROR_FILE_NOT_FOUND);
3369 /***********************************************************************
3370 * GetUrlCacheConfigInfoW (WININET.@)
3372 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3374 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3375 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3379 /***********************************************************************
3380 * GetUrlCacheConfigInfoA (WININET.@)
3382 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3384 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3386 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3387 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3391 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3392 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3393 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3395 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3396 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3397 lpdwGroupInfo, lpReserved);
3401 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3402 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3403 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3405 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3406 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3407 lpdwGroupInfo, lpReserved);
3411 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3412 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3414 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3415 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3419 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3420 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3422 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3423 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3427 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3429 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3433 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3435 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3439 /***********************************************************************
3440 * DeleteIE3Cache (WININET.@)
3442 * Deletes the files used by the IE3 URL caching system.
3445 * hWnd [I] A dummy window.
3446 * hInst [I] Instance of process calling the function.
3447 * lpszCmdLine [I] Options used by function.
3448 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3450 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3452 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3456 /***********************************************************************
3457 * IsUrlCacheEntryExpiredA (WININET.@)
3461 * dwFlags [I] Unknown
3462 * pftLastModified [O] Last modified time
3464 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3466 LPURLCACHE_HEADER pHeader;
3467 struct _HASH_ENTRY * pHashEntry;
3468 const CACHEFILE_ENTRY * pEntry;
3469 const URL_CACHEFILE_ENTRY * pUrlEntry;
3470 URLCACHECONTAINER * pContainer;
3473 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3475 error = URLCacheContainers_FindContainerA(url, &pContainer);
3476 if (error != ERROR_SUCCESS)
3478 SetLastError(error);
3482 error = URLCacheContainer_OpenIndex(pContainer);
3483 if (error != ERROR_SUCCESS)
3485 SetLastError(error);
3489 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3492 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3494 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3495 TRACE("entry %s not found!\n", url);
3496 SetLastError(ERROR_FILE_NOT_FOUND);
3500 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3501 if (pEntry->dwSignature != URL_SIGNATURE)
3503 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3504 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3505 SetLastError(ERROR_FILE_NOT_FOUND);
3509 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3511 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3513 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3518 /***********************************************************************
3519 * IsUrlCacheEntryExpiredW (WININET.@)
3523 * dwFlags [I] Unknown
3524 * pftLastModified [O] Last modified time
3526 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3528 LPURLCACHE_HEADER pHeader;
3529 struct _HASH_ENTRY * pHashEntry;
3530 const CACHEFILE_ENTRY * pEntry;
3531 const URL_CACHEFILE_ENTRY * pUrlEntry;
3532 URLCACHECONTAINER * pContainer;
3535 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3537 error = URLCacheContainers_FindContainerW(url, &pContainer);
3538 if (error != ERROR_SUCCESS)
3540 SetLastError(error);
3544 error = URLCacheContainer_OpenIndex(pContainer);
3545 if (error != ERROR_SUCCESS)
3547 SetLastError(error);
3551 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3554 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3556 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3557 TRACE("entry %s not found!\n", debugstr_w(url));
3558 SetLastError(ERROR_FILE_NOT_FOUND);
3562 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3563 if (pEntry->dwSignature != URL_SIGNATURE)
3565 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3566 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3567 SetLastError(ERROR_FILE_NOT_FOUND);
3571 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3573 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3575 URLCacheContainer_UnlockIndex(pContainer, pHeader);