2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define ENTRY_START_OFFSET 0x4000
66 #define HASHTABLE_SIZE 448
67 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
68 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
71 #define MIN_BLOCK_NO 0x80
72 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
73 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
75 #define HASHTABLE_URL 0
76 #define HASHTABLE_DEL 1
77 #define HASHTABLE_LOCK 2
78 #define HASHTABLE_FREE 3
79 #define HASHTABLE_REDR 5
80 #define HASHTABLE_FLAG_BITS 5
82 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
84 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
85 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
86 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
87 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
88 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
90 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
92 typedef struct _CACHEFILE_ENTRY
96 DWORD dwSignature; /* e.g. "URL " */
97 /* CHAR szSignature[4];
99 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
102 typedef struct _URL_CACHEFILE_ENTRY
104 CACHEFILE_ENTRY CacheFileEntry;
105 FILETIME LastModifiedTime;
106 FILETIME LastAccessTime;
107 WORD wExpiredDate; /* expire date in dos format */
108 WORD wExpiredTime; /* expire time in dos format */
109 DWORD dwUnknown1; /* usually zero */
110 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
111 DWORD dwUnknown2; /* usually zero */
112 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
113 DWORD dwUnknown3; /* usually 0x60 */
114 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
115 BYTE CacheDir; /* index of cache directory this url is stored in */
116 BYTE Unknown4; /* usually zero */
117 WORD wUnknown5; /* usually 0x1010 */
118 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
119 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
120 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
121 DWORD dwHeaderInfoSize;
122 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
123 WORD wLastSyncDate; /* last sync date in dos format */
124 WORD wLastSyncTime; /* last sync time in dos format */
125 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
126 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
127 WORD wUnknownDate; /* usually same as wLastSyncDate */
128 WORD wUnknownTime; /* usually same as wLastSyncTime */
129 DWORD dwUnknown7; /* usually zero */
130 DWORD dwUnknown8; /* usually zero */
131 /* packing to dword align start of next field */
132 /* CHAR szSourceUrlName[]; (url) */
133 /* packing to dword align start of next field */
134 /* CHAR szLocalFileName[]; (local file name excluding path) */
135 /* packing to dword align start of next field */
136 /* CHAR szHeaderInfo[]; (header info) */
137 } URL_CACHEFILE_ENTRY;
145 typedef struct _HASH_CACHEFILE_ENTRY
147 CACHEFILE_ENTRY CacheFileEntry;
149 DWORD dwHashTableNumber;
150 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
151 } HASH_CACHEFILE_ENTRY;
153 typedef struct _DIRECTORY_DATA
156 char filename[DIR_LENGTH];
159 typedef struct _URLCACHE_HEADER
161 char szSignature[28];
163 DWORD dwOffsetFirstHashTable;
164 DWORD dwIndexCapacityInBlocks;
167 ULARGE_INTEGER CacheLimit;
168 ULARGE_INTEGER CacheUsage;
169 ULARGE_INTEGER ExemptUsage;
170 DWORD DirectoryCount; /* number of directory_data's */
171 DIRECTORY_DATA directory_data[1]; /* first directory entry */
172 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
173 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
175 typedef struct _STREAM_HANDLE
181 typedef struct _URLCACHECONTAINER
183 struct list entry; /* part of a list */
184 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
185 LPWSTR path; /* path to url container directory */
186 HANDLE hMapping; /* handle of file mapping */
187 DWORD file_size; /* size of file when mapping was opened */
188 HANDLE hMutex; /* handle of mutex */
192 /* List of all containers available */
193 static struct list UrlContainers = LIST_INIT(UrlContainers);
195 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
197 /***********************************************************************
198 * URLCache_PathToObjectName (Internal)
200 * Converts a path to a name suitable for use as a Win32 object name.
201 * Replaces '\\' characters in-place with the specified character
202 * (usually '_' or '!')
208 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
210 for (; *lpszPath; lpszPath++)
212 if (*lpszPath == '\\')
217 /***********************************************************************
218 * URLCacheContainer_OpenIndex (Internal)
220 * Opens the index file and saves mapping handle in hCacheIndexMapping
223 * ERROR_SUCCESS if succeeded
224 * Any other Win32 error code if failed
227 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer, DWORD blocks_no)
230 WCHAR wszFilePath[MAX_PATH];
231 DWORD dwFileSize, new_file_size;
233 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
234 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
236 WaitForSingleObject(pContainer->hMutex, INFINITE);
238 if (pContainer->hMapping) {
239 ReleaseMutex(pContainer->hMutex);
240 return ERROR_SUCCESS;
243 strcpyW(wszFilePath, pContainer->path);
244 strcatW(wszFilePath, wszIndex);
246 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
247 if (hFile == INVALID_HANDLE_VALUE)
249 /* Maybe the directory wasn't there? Try to create it */
250 if (CreateDirectoryW(pContainer->path, 0))
251 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
253 if (hFile == INVALID_HANDLE_VALUE)
255 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
256 ReleaseMutex(pContainer->hMutex);
257 return GetLastError();
260 dwFileSize = GetFileSize(hFile, NULL);
261 if (dwFileSize == INVALID_FILE_SIZE)
263 ReleaseMutex(pContainer->hMutex);
264 return GetLastError();
267 if (blocks_no < MIN_BLOCK_NO)
268 blocks_no = MIN_BLOCK_NO;
269 else if (blocks_no > MAX_BLOCK_NO)
270 blocks_no = MAX_BLOCK_NO;
271 new_file_size = FILE_SIZE(blocks_no);
273 if (dwFileSize < new_file_size)
275 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
277 char achZeroes[0x1000];
279 DWORD dwError = ERROR_SUCCESS;
281 if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
282 dwError = GetLastError();
284 /* Write zeroes to the entire file so we can safely map it without
285 * fear of getting a SEGV because the disk is full.
287 memset(achZeroes, 0, sizeof(achZeroes));
288 for (dwOffset = dwFileSize; dwOffset<new_file_size && dwError==ERROR_SUCCESS;
289 dwOffset += sizeof(achZeroes))
291 DWORD dwWrite = sizeof(achZeroes);
294 if (new_file_size - dwOffset < dwWrite)
295 dwWrite = new_file_size - dwOffset;
296 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
297 dwWritten != dwWrite)
299 /* If we fail to write, we need to return the error that
300 * cause the problem and also make sure the file is no
301 * longer there, if possible.
303 dwError = GetLastError();
307 if (dwError == ERROR_SUCCESS)
309 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
313 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
315 if (pHeader && dwFileSize)
317 pHeader->dwFileSize = new_file_size;
318 pHeader->dwIndexCapacityInBlocks = blocks_no;
323 WCHAR wszDirPath[MAX_PATH];
326 HASH_CACHEFILE_ENTRY *pHashEntry;
328 /* First set some constants and defaults in the header */
329 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
330 pHeader->dwFileSize = new_file_size;
331 pHeader->dwIndexCapacityInBlocks = blocks_no;
332 /* 127MB - taken from default for Windows 2000 */
333 pHeader->CacheLimit.QuadPart = 0x07ff5400;
334 /* Copied from a Windows 2000 cache index */
335 pHeader->DirectoryCount = 4;
337 /* If the registry has a cache size set, use the registry value */
338 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
341 DWORD len = sizeof(dw);
344 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
345 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
346 keytype == REG_DWORD)
348 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
353 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
355 /* Last step - create the directories */
357 strcpyW(wszDirPath, pContainer->path);
358 pwchDir = wszDirPath + strlenW(wszDirPath);
361 GetSystemTimeAsFileTime(&ft);
363 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
365 pHeader->directory_data[i].dwNumFiles = 0;
369 ULONGLONG n = ft.dwHighDateTime;
371 /* Generate a file name to attempt to create.
372 * This algorithm will create what will appear
373 * to be random and unrelated directory names
374 * of up to 9 characters in length.
377 n += ft.dwLowDateTime;
378 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
380 for (k = 0; k < 8; ++k)
384 /* Dividing by a prime greater than 36 helps
385 * with the appearance of randomness
390 pwchDir[k] = '0' + r;
392 pwchDir[k] = 'A' + (r - 10);
395 if (CreateDirectoryW(wszDirPath, 0))
397 /* The following is OK because we generated an
398 * 8 character directory name made from characters
399 * [A-Z0-9], which are equivalent for all code
400 * pages and for UTF-16
402 for (k = 0; k < 8; ++k)
403 pHeader->directory_data[i].filename[k] = pwchDir[k];
408 /* Give up. The most likely cause of this
409 * is a full disk, but whatever the cause
410 * is, it should be more than apparent that
413 dwError = GetLastError();
419 UnmapViewOfFile(pHeader);
423 dwError = GetLastError();
425 dwFileSize = new_file_size;
426 CloseHandle(hMapping);
430 dwError = GetLastError();
437 DeleteFileW(wszFilePath);
438 ReleaseMutex(pContainer->hMutex);
444 pContainer->file_size = dwFileSize;
445 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
446 URLCache_PathToObjectName(wszFilePath, '_');
447 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
448 if (!pContainer->hMapping)
449 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
451 if (!pContainer->hMapping)
453 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
454 ReleaseMutex(pContainer->hMutex);
455 return GetLastError();
458 ReleaseMutex(pContainer->hMutex);
460 return ERROR_SUCCESS;
463 /***********************************************************************
464 * URLCacheContainer_CloseIndex (Internal)
472 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
474 CloseHandle(pContainer->hMapping);
475 pContainer->hMapping = NULL;
478 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
480 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
481 int cache_prefix_len = strlenW(cache_prefix);
488 pContainer->hMapping = NULL;
489 pContainer->file_size = 0;
491 pContainer->path = heap_strdupW(path);
492 if (!pContainer->path)
494 heap_free(pContainer);
498 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
499 if (!pContainer->cache_prefix)
501 heap_free(pContainer->path);
502 heap_free(pContainer);
506 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
508 CharLowerW(mutex_name);
509 URLCache_PathToObjectName(mutex_name, '!');
511 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
513 ERR("couldn't create mutex (error is %d)\n", GetLastError());
514 heap_free(pContainer->path);
515 heap_free(pContainer);
519 list_add_head(&UrlContainers, &pContainer->entry);
524 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
526 list_remove(&pContainer->entry);
528 URLCacheContainer_CloseIndex(pContainer);
529 CloseHandle(pContainer->hMutex);
530 heap_free(pContainer->path);
531 heap_free(pContainer->cache_prefix);
532 heap_free(pContainer);
535 void URLCacheContainers_CreateDefaults(void)
537 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
538 static const WCHAR UrlPrefix[] = {0};
539 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
540 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
541 static const WCHAR CookieSuffix[] = {0};
542 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
545 int nFolder; /* CSIDL_* constant */
546 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
547 const WCHAR * cache_prefix; /* prefix used to reference the container */
548 } DefaultContainerData[] =
550 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
551 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
552 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
556 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
558 WCHAR wszCachePath[MAX_PATH];
559 WCHAR wszMutexName[MAX_PATH];
560 int path_len, suffix_len;
562 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
564 ERR("Couldn't get path for default container %u\n", i);
567 path_len = strlenW(wszCachePath);
568 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
570 if (path_len + suffix_len + 2 > MAX_PATH)
572 ERR("Path too long\n");
576 wszCachePath[path_len] = '\\';
577 wszCachePath[path_len+1] = 0;
579 strcpyW(wszMutexName, wszCachePath);
583 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
584 wszCachePath[path_len + suffix_len + 1] = '\\';
585 wszCachePath[path_len + suffix_len + 2] = '\0';
588 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
592 void URLCacheContainers_DeleteAll(void)
594 while(!list_empty(&UrlContainers))
595 URLCacheContainer_DeleteContainer(
596 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
600 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
602 URLCACHECONTAINER * pContainer;
604 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
607 return ERROR_INVALID_PARAMETER;
609 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
611 int prefix_len = strlenW(pContainer->cache_prefix);
612 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
614 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
615 *ppContainer = pContainer;
616 return ERROR_SUCCESS;
619 ERR("no container found\n");
620 return ERROR_FILE_NOT_FOUND;
623 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
628 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
629 return ERROR_OUTOFMEMORY;
631 ret = URLCacheContainers_FindContainerW(url, ppContainer);
636 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
639 URLCACHECONTAINER * pContainer;
641 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
643 /* non-NULL search pattern only returns one container ever */
644 if (lpwszSearchPattern && dwIndex > 0)
647 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
649 if (lpwszSearchPattern)
651 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
653 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
654 *ppContainer = pContainer;
662 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
663 *ppContainer = pContainer;
672 /***********************************************************************
673 * URLCacheContainer_LockIndex (Internal)
675 * Locks the index for system-wide exclusive access.
678 * Cache file header if successful
679 * NULL if failed and calls SetLastError.
681 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
685 URLCACHE_HEADER * pHeader;
689 WaitForSingleObject(pContainer->hMutex, INFINITE);
691 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
695 ReleaseMutex(pContainer->hMutex);
696 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
699 pHeader = (URLCACHE_HEADER *)pIndexData;
701 /* file has grown - we need to remap to prevent us getting
702 * access violations when we try and access beyond the end
703 * of the memory mapped file */
704 if (pHeader->dwFileSize != pContainer->file_size)
706 UnmapViewOfFile( pHeader );
707 URLCacheContainer_CloseIndex(pContainer);
708 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
709 if (error != ERROR_SUCCESS)
711 ReleaseMutex(pContainer->hMutex);
715 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
719 ReleaseMutex(pContainer->hMutex);
720 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
723 pHeader = (URLCACHE_HEADER *)pIndexData;
726 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
728 for (index = 0; index < pHeader->DirectoryCount; index++)
730 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
736 /***********************************************************************
737 * URLCacheContainer_UnlockIndex (Internal)
740 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
743 ReleaseMutex(pContainer->hMutex);
744 return UnmapViewOfFile(pHeader);
747 /***********************************************************************
748 * URLCacheContainer_CleanIndex (Internal)
750 * This function is meant to make place in index file by removing old
751 * entries and resizing the file.
753 * CAUTION: file view may get mapped to new memory
754 * TODO: implement entries cleaning
757 * ERROR_SUCCESS when new memory is available
758 * error code otherwise
760 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
762 URLCACHE_HEADER *header = *file_view;
765 FIXME("(%s %s) semi-stub\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
767 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
768 WARN("index file has maximal size\n");
769 return ERROR_NOT_ENOUGH_MEMORY;
772 URLCacheContainer_CloseIndex(container);
773 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
774 if(ret != ERROR_SUCCESS)
776 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
778 return GetLastError();
780 UnmapViewOfFile(*file_view);
782 return ERROR_SUCCESS;
786 #define CHAR_BIT (8 * sizeof(CHAR))
789 /***********************************************************************
790 * URLCache_Allocation_BlockIsFree (Internal)
792 * Is the specified block number free?
799 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
801 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
802 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
805 /***********************************************************************
806 * URLCache_Allocation_BlockFree (Internal)
808 * Marks the specified block as free
814 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
816 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
817 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
820 /***********************************************************************
821 * URLCache_Allocation_BlockAllocate (Internal)
823 * Marks the specified block as allocated
829 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
831 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
832 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
835 /***********************************************************************
836 * URLCache_FindFirstFreeEntry (Internal)
838 * Finds and allocates the first block of free space big enough and
839 * sets ppEntry to point to it.
842 * ERROR_SUCCESS when free memory block was found
843 * Any other Win32 error code if the entry could not be added
846 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
848 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
851 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
853 for (dwFreeCounter = 0;
854 dwFreeCounter < dwBlocksNeeded &&
855 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
856 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
858 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
860 if (dwFreeCounter == dwBlocksNeeded)
863 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
864 for (index = 0; index < dwBlocksNeeded; index++)
865 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
866 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
867 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
868 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
869 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
870 return ERROR_SUCCESS;
874 return ERROR_HANDLE_DISK_FULL;
877 /***********************************************************************
878 * URLCache_DeleteEntry (Internal)
880 * Deletes the specified entry and frees the space allocated to it
883 * TRUE if it succeeded
887 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
891 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
893 /* update allocation table */
894 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
895 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
896 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
901 /***********************************************************************
902 * URLCache_LocalFileNameToPathW (Internal)
904 * Copies the full path to the specified buffer given the local file
905 * name and the index of the directory it is in. Always sets value in
906 * lpBufferSize to the required buffer size (in bytes).
909 * TRUE if the buffer was big enough
910 * FALSE if the buffer was too small
913 static BOOL URLCache_LocalFileNameToPathW(
914 const URLCACHECONTAINER * pContainer,
915 LPCURLCACHE_HEADER pHeader,
916 LPCSTR szLocalFileName,
922 int path_len = strlenW(pContainer->path);
923 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
924 if (Directory >= pHeader->DirectoryCount)
930 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
931 if (nRequired <= *lpBufferSize)
935 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
936 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
937 wszPath[dir_len + path_len] = '\\';
938 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
939 *lpBufferSize = nRequired;
942 *lpBufferSize = nRequired;
946 /***********************************************************************
947 * URLCache_LocalFileNameToPathA (Internal)
949 * Copies the full path to the specified buffer given the local file
950 * name and the index of the directory it is in. Always sets value in
951 * lpBufferSize to the required buffer size.
954 * TRUE if the buffer was big enough
955 * FALSE if the buffer was too small
958 static BOOL URLCache_LocalFileNameToPathA(
959 const URLCACHECONTAINER * pContainer,
960 LPCURLCACHE_HEADER pHeader,
961 LPCSTR szLocalFileName,
967 int path_len, file_name_len, dir_len;
969 if (Directory >= pHeader->DirectoryCount)
975 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
976 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
977 dir_len = DIR_LENGTH;
979 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
980 if (nRequired < *lpBufferSize)
982 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
983 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
984 szPath[path_len + dir_len] = '\\';
985 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
986 *lpBufferSize = nRequired;
989 *lpBufferSize = nRequired;
993 /* Just like DosDateTimeToFileTime, except that it also maps the special
994 * case of a DOS date/time of (0,0) to a filetime of (0,0).
996 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
999 if (!fatdate && !fattime)
1000 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1002 DosDateTimeToFileTime(fatdate, fattime, ft);
1005 /***********************************************************************
1006 * URLCache_CopyEntry (Internal)
1008 * Copies an entry from the cache index file to the Win32 structure
1011 * ERROR_SUCCESS if the buffer was big enough
1012 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1015 static DWORD URLCache_CopyEntry(
1016 URLCACHECONTAINER * pContainer,
1017 LPCURLCACHE_HEADER pHeader,
1018 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1019 LPDWORD lpdwBufferSize,
1020 const URL_CACHEFILE_ENTRY * pUrlEntry,
1024 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1026 if (*lpdwBufferSize >= dwRequiredSize)
1028 lpCacheEntryInfo->lpHeaderInfo = NULL;
1029 lpCacheEntryInfo->lpszFileExtension = NULL;
1030 lpCacheEntryInfo->lpszLocalFileName = NULL;
1031 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1032 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1033 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1034 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1035 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1036 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1037 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1038 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1039 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1040 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1041 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1042 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1043 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1044 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1045 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1048 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1049 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1050 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1052 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1054 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1055 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1057 /* FIXME: is source url optional? */
1058 if (*lpdwBufferSize >= dwRequiredSize)
1060 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1062 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1064 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1066 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1069 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1070 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1071 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1073 if (pUrlEntry->dwOffsetLocalName)
1075 LONG nLocalFilePathSize;
1076 LPSTR lpszLocalFileName;
1077 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1078 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1079 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1080 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1082 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1084 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1086 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1087 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1088 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1090 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1092 if (*lpdwBufferSize >= dwRequiredSize)
1094 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1095 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1096 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1098 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1099 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1100 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1102 if (pUrlEntry->dwOffsetFileExtension)
1107 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1109 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1110 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1112 if (*lpdwBufferSize >= dwRequiredSize)
1114 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1116 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1118 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1121 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1122 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1123 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1126 if (dwRequiredSize > *lpdwBufferSize)
1128 *lpdwBufferSize = dwRequiredSize;
1129 return ERROR_INSUFFICIENT_BUFFER;
1131 *lpdwBufferSize = dwRequiredSize;
1132 return ERROR_SUCCESS;
1135 /* Just like FileTimeToDosDateTime, except that it also maps the special
1136 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1138 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1141 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1142 *fatdate = *fattime = 0;
1144 FileTimeToDosDateTime(ft, fatdate, fattime);
1147 /***********************************************************************
1148 * URLCache_SetEntryInfo (Internal)
1150 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1151 * according to the flags set by dwFieldControl.
1154 * ERROR_SUCCESS if the buffer was big enough
1155 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1158 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1160 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1161 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1162 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1163 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1164 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1165 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1166 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1167 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1168 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1169 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1170 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1171 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1172 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1173 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1174 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1175 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1177 return ERROR_SUCCESS;
1180 /***********************************************************************
1181 * URLCache_HashKey (Internal)
1183 * Returns the hash key for a given string
1186 * hash key for the string
1189 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1191 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1192 * but the algorithm and result are not the same!
1194 static const unsigned char lookupTable[256] =
1196 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1197 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1198 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1199 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1200 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1201 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1202 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1203 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1204 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1205 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1206 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1207 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1208 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1209 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1210 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1211 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1212 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1213 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1214 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1215 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1216 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1217 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1218 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1219 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1220 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1221 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1222 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1223 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1224 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1225 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1226 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1227 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1232 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1233 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1235 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1237 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1238 key[i] = lookupTable[*lpszKey ^ key[i]];
1241 return *(DWORD *)key;
1244 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1246 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1249 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1251 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1252 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1253 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1256 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1258 /* structure of hash table:
1259 * 448 entries divided into 64 blocks
1260 * each block therefore contains a chain of 7 key/offset pairs
1261 * how position in table is calculated:
1262 * 1. the url is hashed in helper function
1263 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1264 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1267 * there can be multiple hash tables in the file and the offset to
1268 * the next one is stored in the header of the hash table
1270 DWORD key = URLCache_HashKey(lpszUrl);
1271 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1272 HASH_CACHEFILE_ENTRY * pHashEntry;
1273 DWORD dwHashTableNumber = 0;
1275 key >>= HASHTABLE_FLAG_BITS;
1277 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1278 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1279 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1282 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1284 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1287 /* make sure that it is in fact a hash entry */
1288 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1290 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1294 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1296 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1297 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1299 /* FIXME: we should make sure that this is the right element
1300 * before returning and claiming that it is. We can do this
1301 * by doing a simple compare between the URL we were given
1302 * and the URL stored in the entry. However, this assumes
1303 * we know the format of all the entries stored in the
1305 *ppHashEntry = pHashElement;
1313 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1318 urlA = heap_strdupWtoA(lpszUrl);
1321 SetLastError(ERROR_OUTOFMEMORY);
1325 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1330 /***********************************************************************
1331 * URLCache_HashEntrySetFlags (Internal)
1333 * Sets special bits in hash key
1339 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1341 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1344 /***********************************************************************
1345 * URLCache_DeleteEntryFromHash (Internal)
1347 * Searches all the hash tables in the index for the given URL and
1348 * then if found deletes the entry.
1351 * TRUE if the entry was found
1352 * FALSE if the entry could not be found
1355 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1357 pHashEntry->dwHashKey = HASHTABLE_DEL;
1361 /***********************************************************************
1362 * URLCache_AddEntryToHash (Internal)
1364 * Searches all the hash tables for a free slot based on the offset
1365 * generated from the hash key. If a free slot is found, the offset and
1366 * key are entered into the hash table.
1369 * ERROR_SUCCESS if the entry was added
1370 * Any other Win32 error code if the entry could not be added
1373 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1375 /* see URLCache_FindEntryInHash for structure of hash tables */
1377 DWORD key = URLCache_HashKey(lpszUrl);
1378 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1379 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1380 DWORD dwHashTableNumber = 0;
1383 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1385 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1386 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1387 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1390 pHashPrev = pHashEntry;
1392 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1394 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1397 /* make sure that it is in fact a hash entry */
1398 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1400 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1404 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1406 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1407 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1409 pHashElement->dwHashKey = key;
1410 pHashElement->dwOffsetEntry = dwOffsetEntry;
1411 return ERROR_SUCCESS;
1415 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1416 if (error != ERROR_SUCCESS)
1419 pHashEntry->HashTable[offset].dwHashKey = key;
1420 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1421 return ERROR_SUCCESS;
1424 /***********************************************************************
1425 * URLCache_CreateHashTable (Internal)
1427 * Creates a new hash table in free space and adds it to the chain of existing
1431 * ERROR_SUCCESS if the hash table was created
1432 * ERROR_DISK_FULL if the hash table could not be created
1435 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1437 DWORD dwOffset, error;
1440 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1443 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1446 pPrevHash->dwAddressNext = dwOffset;
1448 pHeader->dwOffsetFirstHashTable = dwOffset;
1449 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1450 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1451 (*ppHash)->dwAddressNext = 0;
1452 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1453 for (i = 0; i < HASHTABLE_SIZE; i++)
1455 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1456 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1458 return ERROR_SUCCESS;
1461 /***********************************************************************
1462 * URLCache_EnumHashTables (Internal)
1464 * Enumerates the hash tables in a container.
1467 * TRUE if an entry was found
1468 * FALSE if there are no more tables to enumerate.
1471 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1473 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1474 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1475 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1477 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1478 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1480 /* make sure that it is in fact a hash entry */
1481 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1483 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1484 (*pdwHashTableNumber)++;
1488 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1494 /***********************************************************************
1495 * URLCache_EnumHashTableEntries (Internal)
1497 * Enumerates entries in a hash table and returns the next non-free entry.
1500 * TRUE if an entry was found
1501 * FALSE if the hash table is empty or there are no more entries to
1505 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1506 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1508 for (; *index < HASHTABLE_SIZE ; (*index)++)
1510 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1513 *ppHashEntry = &pHashEntry->HashTable[*index];
1514 TRACE("entry found %d\n", *index);
1517 TRACE("no more entries (%d)\n", *index);
1521 /***********************************************************************
1522 * URLCache_DeleteCacheDirectory (Internal)
1524 * Erase a directory containing an URL cache.
1527 * TRUE success, FALSE failure/aborted.
1530 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1533 WCHAR path[MAX_PATH + 1];
1534 SHFILEOPSTRUCTW shfos;
1537 path_len = strlenW(lpszPath);
1538 if (path_len >= MAX_PATH)
1540 strcpyW(path, lpszPath);
1541 path[path_len + 1] = 0; /* double-NUL-terminate path */
1544 shfos.wFunc = FO_DELETE;
1548 shfos.fAnyOperationsAborted = FALSE;
1549 ret = SHFileOperationW(&shfos);
1551 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1552 return !(ret || shfos.fAnyOperationsAborted);
1555 /***********************************************************************
1556 * URLCache_IsLocked (Internal)
1558 * Checks if entry is locked. Unlocks it if possible.
1560 static BOOL URLCache_IsLocked(struct _HASH_ENTRY *hash_entry, URL_CACHEFILE_ENTRY *url_entry)
1563 ULARGE_INTEGER acc_time, time;
1565 if ((hash_entry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1568 GetSystemTimeAsFileTime(&cur_time);
1569 time.u.LowPart = cur_time.dwLowDateTime;
1570 time.u.HighPart = cur_time.dwHighDateTime;
1572 acc_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
1573 acc_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
1575 time.QuadPart -= acc_time.QuadPart;
1577 /* check if entry was locked for at least a day */
1578 if(time.QuadPart > (ULONGLONG)24*60*60*10000000) {
1579 URLCache_HashEntrySetFlags(hash_entry, HASHTABLE_URL);
1580 url_entry->dwUseCount = 0;
1587 /***********************************************************************
1588 * FreeUrlCacheSpaceW (WININET.@)
1590 * Frees up some cache.
1593 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1594 * dwSize [I] How much space to free up.
1595 * dwSizeType [I] How to interpret dwSize.
1598 * TRUE success. FALSE failure.
1601 * This implementation just retrieves the path of the cache directory, and
1602 * deletes its contents from the filesystem. The correct approach would
1603 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1605 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1607 URLCACHECONTAINER * pContainer;
1609 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1611 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1612 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1616 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1618 /* The URL cache has prefix L"" (unlike Cookies and History) */
1619 if (pContainer->cache_prefix[0] == 0)
1623 WaitForSingleObject(pContainer->hMutex, INFINITE);
1625 /* unlock, delete, recreate and lock cache */
1626 URLCacheContainer_CloseIndex(pContainer);
1627 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1628 ret_open = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1630 ReleaseMutex(pContainer->hMutex);
1631 return ret_del && (ret_open == ERROR_SUCCESS);
1637 /***********************************************************************
1638 * FreeUrlCacheSpaceA (WININET.@)
1640 * See FreeUrlCacheSpaceW.
1642 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1645 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1646 if (lpszCachePath == NULL || path != NULL)
1647 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1652 /***********************************************************************
1653 * GetUrlCacheEntryInfoExA (WININET.@)
1656 BOOL WINAPI GetUrlCacheEntryInfoExA(
1658 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1659 LPDWORD lpdwCacheEntryInfoBufSize,
1661 LPDWORD lpdwReserved,
1665 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1666 debugstr_a(lpszUrl),
1668 lpdwCacheEntryInfoBufSize,
1674 if ((lpszReserved != NULL) ||
1675 (lpdwReserved != NULL) ||
1676 (lpReserved != NULL))
1678 ERR("Reserved value was not 0\n");
1679 SetLastError(ERROR_INVALID_PARAMETER);
1684 FIXME("Undocumented flag(s): %x\n", dwFlags);
1685 SetLastError(ERROR_FILE_NOT_FOUND);
1688 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1691 /***********************************************************************
1692 * GetUrlCacheEntryInfoA (WININET.@)
1695 BOOL WINAPI GetUrlCacheEntryInfoA(
1696 IN LPCSTR lpszUrlName,
1697 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1698 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1701 LPURLCACHE_HEADER pHeader;
1702 struct _HASH_ENTRY * pHashEntry;
1703 const CACHEFILE_ENTRY * pEntry;
1704 const URL_CACHEFILE_ENTRY * pUrlEntry;
1705 URLCACHECONTAINER * pContainer;
1708 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1710 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1711 if (error != ERROR_SUCCESS)
1713 SetLastError(error);
1717 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1718 if (error != ERROR_SUCCESS)
1720 SetLastError(error);
1724 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1727 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1729 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1730 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1731 SetLastError(ERROR_FILE_NOT_FOUND);
1735 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1736 if (pEntry->dwSignature != URL_SIGNATURE)
1738 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1739 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1740 SetLastError(ERROR_FILE_NOT_FOUND);
1744 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1745 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1746 if (pUrlEntry->dwOffsetHeaderInfo)
1747 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1749 if (lpdwCacheEntryInfoBufferSize)
1751 if (!lpCacheEntryInfo)
1752 *lpdwCacheEntryInfoBufferSize = 0;
1754 error = URLCache_CopyEntry(
1758 lpdwCacheEntryInfoBufferSize,
1761 if (error != ERROR_SUCCESS)
1763 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1764 SetLastError(error);
1767 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1770 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1775 /***********************************************************************
1776 * GetUrlCacheEntryInfoW (WININET.@)
1779 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1780 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1781 LPDWORD lpdwCacheEntryInfoBufferSize)
1783 LPURLCACHE_HEADER pHeader;
1784 struct _HASH_ENTRY * pHashEntry;
1785 const CACHEFILE_ENTRY * pEntry;
1786 const URL_CACHEFILE_ENTRY * pUrlEntry;
1787 URLCACHECONTAINER * pContainer;
1790 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1792 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1793 if (error != ERROR_SUCCESS)
1795 SetLastError(error);
1799 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1800 if (error != ERROR_SUCCESS)
1802 SetLastError(error);
1806 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1809 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1811 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1812 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1813 SetLastError(ERROR_FILE_NOT_FOUND);
1817 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1818 if (pEntry->dwSignature != URL_SIGNATURE)
1820 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1821 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1822 SetLastError(ERROR_FILE_NOT_FOUND);
1826 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1827 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1828 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1830 if (lpdwCacheEntryInfoBufferSize)
1832 if (!lpCacheEntryInfo)
1833 *lpdwCacheEntryInfoBufferSize = 0;
1835 error = URLCache_CopyEntry(
1838 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1839 lpdwCacheEntryInfoBufferSize,
1841 TRUE /* UNICODE */);
1842 if (error != ERROR_SUCCESS)
1844 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1845 SetLastError(error);
1848 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1851 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1856 /***********************************************************************
1857 * GetUrlCacheEntryInfoExW (WININET.@)
1860 BOOL WINAPI GetUrlCacheEntryInfoExW(
1862 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1863 LPDWORD lpdwCacheEntryInfoBufSize,
1864 LPWSTR lpszReserved,
1865 LPDWORD lpdwReserved,
1869 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1870 debugstr_w(lpszUrl),
1872 lpdwCacheEntryInfoBufSize,
1878 if ((lpszReserved != NULL) ||
1879 (lpdwReserved != NULL) ||
1880 (lpReserved != NULL))
1882 ERR("Reserved value was not 0\n");
1883 SetLastError(ERROR_INVALID_PARAMETER);
1888 FIXME("Undocumented flag(s): %x\n", dwFlags);
1889 SetLastError(ERROR_FILE_NOT_FOUND);
1892 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1895 /***********************************************************************
1896 * SetUrlCacheEntryInfoA (WININET.@)
1898 BOOL WINAPI SetUrlCacheEntryInfoA(
1900 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1901 DWORD dwFieldControl)
1903 LPURLCACHE_HEADER pHeader;
1904 struct _HASH_ENTRY * pHashEntry;
1905 CACHEFILE_ENTRY * pEntry;
1906 URLCACHECONTAINER * pContainer;
1909 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1911 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1912 if (error != ERROR_SUCCESS)
1914 SetLastError(error);
1918 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1919 if (error != ERROR_SUCCESS)
1921 SetLastError(error);
1925 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1928 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1930 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1932 SetLastError(ERROR_FILE_NOT_FOUND);
1936 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1937 if (pEntry->dwSignature != URL_SIGNATURE)
1939 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1940 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1941 SetLastError(ERROR_FILE_NOT_FOUND);
1945 URLCache_SetEntryInfo(
1946 (URL_CACHEFILE_ENTRY *)pEntry,
1947 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1950 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1955 /***********************************************************************
1956 * SetUrlCacheEntryInfoW (WININET.@)
1958 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1960 LPURLCACHE_HEADER pHeader;
1961 struct _HASH_ENTRY * pHashEntry;
1962 CACHEFILE_ENTRY * pEntry;
1963 URLCACHECONTAINER * pContainer;
1966 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1968 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1969 if (error != ERROR_SUCCESS)
1971 SetLastError(error);
1975 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1976 if (error != ERROR_SUCCESS)
1978 SetLastError(error);
1982 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1985 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1987 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1988 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1989 SetLastError(ERROR_FILE_NOT_FOUND);
1993 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1994 if (pEntry->dwSignature != URL_SIGNATURE)
1996 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1997 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1998 SetLastError(ERROR_FILE_NOT_FOUND);
2002 URLCache_SetEntryInfo(
2003 (URL_CACHEFILE_ENTRY *)pEntry,
2007 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2012 /***********************************************************************
2013 * RetrieveUrlCacheEntryFileA (WININET.@)
2016 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2017 IN LPCSTR lpszUrlName,
2018 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2019 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2023 LPURLCACHE_HEADER pHeader;
2024 struct _HASH_ENTRY * pHashEntry;
2025 CACHEFILE_ENTRY * pEntry;
2026 URL_CACHEFILE_ENTRY * pUrlEntry;
2027 URLCACHECONTAINER * pContainer;
2030 TRACE("(%s, %p, %p, 0x%08x)\n",
2031 debugstr_a(lpszUrlName),
2033 lpdwCacheEntryInfoBufferSize,
2036 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2037 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2039 SetLastError(ERROR_INVALID_PARAMETER);
2043 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2044 if (error != ERROR_SUCCESS)
2046 SetLastError(error);
2050 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2051 if (error != ERROR_SUCCESS)
2053 SetLastError(error);
2057 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2060 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2062 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2063 TRACE("entry %s not found!\n", lpszUrlName);
2064 SetLastError(ERROR_FILE_NOT_FOUND);
2068 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2069 if (pEntry->dwSignature != URL_SIGNATURE)
2071 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2072 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2073 SetLastError(ERROR_FILE_NOT_FOUND);
2077 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2078 if (!pUrlEntry->dwOffsetLocalName)
2080 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2081 SetLastError(ERROR_INVALID_DATA);
2085 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2086 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2088 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2089 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2091 if (error != ERROR_SUCCESS)
2093 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2094 SetLastError(error);
2097 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2099 pUrlEntry->dwHitRate++;
2100 pUrlEntry->dwUseCount++;
2101 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2102 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2104 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2109 /***********************************************************************
2110 * RetrieveUrlCacheEntryFileW (WININET.@)
2113 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2114 IN LPCWSTR lpszUrlName,
2115 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2116 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2120 LPURLCACHE_HEADER pHeader;
2121 struct _HASH_ENTRY * pHashEntry;
2122 CACHEFILE_ENTRY * pEntry;
2123 URL_CACHEFILE_ENTRY * pUrlEntry;
2124 URLCACHECONTAINER * pContainer;
2127 TRACE("(%s, %p, %p, 0x%08x)\n",
2128 debugstr_w(lpszUrlName),
2130 lpdwCacheEntryInfoBufferSize,
2133 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2134 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2136 SetLastError(ERROR_INVALID_PARAMETER);
2140 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2141 if (error != ERROR_SUCCESS)
2143 SetLastError(error);
2147 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2148 if (error != ERROR_SUCCESS)
2150 SetLastError(error);
2154 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2157 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2159 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2160 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2161 SetLastError(ERROR_FILE_NOT_FOUND);
2165 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2166 if (pEntry->dwSignature != URL_SIGNATURE)
2168 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2169 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2170 SetLastError(ERROR_FILE_NOT_FOUND);
2174 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2175 if (!pUrlEntry->dwOffsetLocalName)
2177 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2178 SetLastError(ERROR_INVALID_DATA);
2182 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2183 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2185 error = URLCache_CopyEntry(
2188 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2189 lpdwCacheEntryInfoBufferSize,
2191 TRUE /* UNICODE */);
2192 if (error != ERROR_SUCCESS)
2194 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2195 SetLastError(error);
2198 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2200 pUrlEntry->dwHitRate++;
2201 pUrlEntry->dwUseCount++;
2202 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2203 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2205 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2210 static BOOL DeleteUrlCacheEntryInternal(const URLCACHECONTAINER * pContainer,
2211 LPURLCACHE_HEADER pHeader, struct _HASH_ENTRY *pHashEntry)
2213 CACHEFILE_ENTRY * pEntry;
2214 URL_CACHEFILE_ENTRY * pUrlEntry;
2215 WCHAR path[MAX_PATH];
2216 LONG path_size = sizeof(path);
2218 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2219 if (pEntry->dwSignature != URL_SIGNATURE)
2221 FIXME("Trying to delete entry of unknown format %s\n",
2222 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2223 SetLastError(ERROR_FILE_NOT_FOUND);
2227 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2228 if(URLCache_IsLocked(pHashEntry, pUrlEntry))
2230 TRACE("Trying to delete locked entry\n");
2231 pUrlEntry->CacheEntryType |= PENDING_DELETE_CACHE_ENTRY;
2232 SetLastError(ERROR_SHARING_VIOLATION);
2236 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2238 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2239 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2241 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2243 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2244 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2246 pHeader->ExemptUsage.QuadPart = 0;
2250 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2251 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2253 pHeader->CacheUsage.QuadPart = 0;
2256 if (pUrlEntry->dwOffsetLocalName && URLCache_LocalFileNameToPathW(pContainer, pHeader,
2257 (LPCSTR)pUrlEntry+pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, path, &path_size))
2262 URLCache_DeleteEntry(pHeader, pEntry);
2263 URLCache_DeleteEntryFromHash(pHashEntry);
2267 /***********************************************************************
2268 * UnlockUrlCacheEntryFileA (WININET.@)
2271 BOOL WINAPI UnlockUrlCacheEntryFileA(
2272 IN LPCSTR lpszUrlName,
2276 LPURLCACHE_HEADER pHeader;
2277 struct _HASH_ENTRY * pHashEntry;
2278 CACHEFILE_ENTRY * pEntry;
2279 URL_CACHEFILE_ENTRY * pUrlEntry;
2280 URLCACHECONTAINER * pContainer;
2283 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2287 ERR("dwReserved != 0\n");
2288 SetLastError(ERROR_INVALID_PARAMETER);
2292 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2293 if (error != ERROR_SUCCESS)
2295 SetLastError(error);
2299 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2300 if (error != ERROR_SUCCESS)
2302 SetLastError(error);
2306 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2309 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2311 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2312 TRACE("entry %s not found!\n", lpszUrlName);
2313 SetLastError(ERROR_FILE_NOT_FOUND);
2317 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2318 if (pEntry->dwSignature != URL_SIGNATURE)
2320 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2321 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2322 SetLastError(ERROR_FILE_NOT_FOUND);
2326 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2328 if (pUrlEntry->dwUseCount == 0)
2330 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2333 pUrlEntry->dwUseCount--;
2334 if (!pUrlEntry->dwUseCount)
2336 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2337 if (pUrlEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)
2338 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2341 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2346 /***********************************************************************
2347 * UnlockUrlCacheEntryFileW (WININET.@)
2350 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2352 LPURLCACHE_HEADER pHeader;
2353 struct _HASH_ENTRY * pHashEntry;
2354 CACHEFILE_ENTRY * pEntry;
2355 URL_CACHEFILE_ENTRY * pUrlEntry;
2356 URLCACHECONTAINER * pContainer;
2359 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2363 ERR("dwReserved != 0\n");
2364 SetLastError(ERROR_INVALID_PARAMETER);
2368 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2369 if (error != ERROR_SUCCESS)
2371 SetLastError(error);
2375 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2376 if (error != ERROR_SUCCESS)
2378 SetLastError(error);
2382 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2385 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2387 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2388 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2389 SetLastError(ERROR_FILE_NOT_FOUND);
2393 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2394 if (pEntry->dwSignature != URL_SIGNATURE)
2396 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2397 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2398 SetLastError(ERROR_FILE_NOT_FOUND);
2402 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2404 if (pUrlEntry->dwUseCount == 0)
2406 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2409 pUrlEntry->dwUseCount--;
2410 if (!pUrlEntry->dwUseCount)
2411 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2413 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2418 /***********************************************************************
2419 * CreateUrlCacheEntryA (WININET.@)
2422 BOOL WINAPI CreateUrlCacheEntryA(
2423 IN LPCSTR lpszUrlName,
2424 IN DWORD dwExpectedFileSize,
2425 IN LPCSTR lpszFileExtension,
2426 OUT LPSTR lpszFileName,
2431 WCHAR *file_extension = NULL;
2432 WCHAR file_name[MAX_PATH];
2433 BOOL bSuccess = FALSE;
2436 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2437 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2439 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2441 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2443 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2445 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2451 dwError = GetLastError();
2456 dwError = GetLastError();
2458 heap_free(file_extension);
2462 dwError = GetLastError();
2464 heap_free(url_name);
2465 if (!bSuccess) SetLastError(dwError);
2469 /***********************************************************************
2470 * CreateUrlCacheEntryW (WININET.@)
2473 BOOL WINAPI CreateUrlCacheEntryW(
2474 IN LPCWSTR lpszUrlName,
2475 IN DWORD dwExpectedFileSize,
2476 IN LPCWSTR lpszFileExtension,
2477 OUT LPWSTR lpszFileName,
2481 URLCACHECONTAINER * pContainer;
2482 LPURLCACHE_HEADER pHeader;
2483 CHAR szFile[MAX_PATH];
2484 WCHAR szExtension[MAX_PATH];
2485 LPCWSTR lpszUrlPart;
2487 LPCWSTR lpszFileNameExtension;
2488 LPWSTR lpszFileNameNoPath;
2490 int countnoextension;
2493 BOOL bFound = FALSE;
2499 static const WCHAR szWWW[] = {'w','w','w',0};
2500 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2502 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2503 debugstr_w(lpszUrlName),
2505 debugstr_w(lpszFileExtension),
2510 FIXME("dwReserved 0x%08x\n", dwReserved);
2512 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2514 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2517 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2519 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2521 lpszUrlEnd = lpszUrlPart;
2523 for (lpszUrlPart = lpszUrlEnd;
2524 (lpszUrlPart >= lpszUrlName);
2527 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2534 if (!lstrcmpW(lpszUrlPart, szWWW))
2536 lpszUrlPart += lstrlenW(szWWW);
2539 count = lpszUrlEnd - lpszUrlPart;
2541 if (bFound && (count < MAX_PATH))
2543 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2547 while(len && szFile[--len] == '/') szFile[len] = '\0';
2549 /* FIXME: get rid of illegal characters like \, / and : */
2553 FIXME("need to generate a random filename\n");
2556 TRACE("File name: %s\n", debugstr_a(szFile));
2558 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2559 if (error != ERROR_SUCCESS)
2561 SetLastError(error);
2565 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2566 if (error != ERROR_SUCCESS)
2568 SetLastError(error);
2572 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2575 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2577 lBufferSize = MAX_PATH * sizeof(WCHAR);
2578 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2580 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2581 debugstr_a(szFile), lBufferSize);
2582 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2586 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2588 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2589 lpszFileNameNoPath >= lpszFileName;
2590 --lpszFileNameNoPath)
2592 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2596 countnoextension = lstrlenW(lpszFileNameNoPath);
2597 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2598 if (lpszFileNameExtension)
2599 countnoextension -= lstrlenW(lpszFileNameExtension);
2600 *szExtension = '\0';
2602 if (lpszFileExtension)
2604 szExtension[0] = '.';
2605 lstrcpyW(szExtension+1, lpszFileExtension);
2608 for (i = 0; i < 255; i++)
2610 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2613 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2614 for (p = lpszFileNameNoPath + 1; *p; p++)
2620 case '/': case '\\':
2627 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2629 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2630 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2631 if (hFile != INVALID_HANDLE_VALUE)
2638 GetSystemTimeAsFileTime(&ft);
2639 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2641 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2642 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2643 if (hFile != INVALID_HANDLE_VALUE)
2649 WARN("Could not find a unique filename\n");
2654 /***********************************************************************
2655 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2657 * The bug we are compensating for is that some drongo at Microsoft
2658 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2659 * As a consequence, CommitUrlCacheEntryA has been effectively
2660 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2661 * is still defined as LPCWSTR. The result (other than madness) is
2662 * that we always need to store lpHeaderInfo in CP_ACP rather than
2663 * in UTF16, and we need to avoid converting lpHeaderInfo in
2664 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2665 * result will lose data for arbitrary binary data.
2668 static BOOL CommitUrlCacheEntryInternal(
2669 IN LPCWSTR lpszUrlName,
2670 IN LPCWSTR lpszLocalFileName,
2671 IN FILETIME ExpireTime,
2672 IN FILETIME LastModifiedTime,
2673 IN DWORD CacheEntryType,
2674 IN LPBYTE lpHeaderInfo,
2675 IN DWORD dwHeaderSize,
2676 IN LPCWSTR lpszFileExtension,
2677 IN LPCWSTR lpszOriginalUrl
2680 URLCACHECONTAINER * pContainer;
2681 LPURLCACHE_HEADER pHeader;
2682 struct _HASH_ENTRY * pHashEntry;
2683 CACHEFILE_ENTRY * pEntry;
2684 URL_CACHEFILE_ENTRY * pUrlEntry;
2685 DWORD url_entry_offset;
2686 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2687 DWORD dwOffsetLocalFileName = 0;
2688 DWORD dwOffsetHeader = 0;
2689 DWORD dwOffsetFileExtension = 0;
2690 LARGE_INTEGER file_size;
2691 BYTE cDirectory = 0;
2692 char achFile[MAX_PATH];
2693 LPSTR lpszUrlNameA = NULL;
2694 LPSTR lpszFileExtensionA = NULL;
2695 char *pchLocalFileName = 0;
2697 DWORD exempt_delta = 0;
2700 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2701 debugstr_w(lpszUrlName),
2702 debugstr_w(lpszLocalFileName),
2706 debugstr_w(lpszFileExtension),
2707 debugstr_w(lpszOriginalUrl));
2709 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2711 SetLastError(ERROR_INVALID_PARAMETER);
2714 if (lpszOriginalUrl)
2715 WARN(": lpszOriginalUrl ignored\n");
2717 file_size.QuadPart = 0;
2718 if (lpszLocalFileName)
2722 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2723 if (hFile == INVALID_HANDLE_VALUE)
2725 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2730 if (!GetFileSizeEx(hFile, &file_size))
2732 ERR("couldn't get file size (error is %d)\n", GetLastError());
2740 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2741 if (error != ERROR_SUCCESS)
2743 SetLastError(error);
2747 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2748 if (error != ERROR_SUCCESS)
2750 SetLastError(error);
2754 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2757 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2760 error = GetLastError();
2764 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2766 error = GetLastError();
2770 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2772 URL_CACHEFILE_ENTRY *pUrlEntry = (URL_CACHEFILE_ENTRY*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2773 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
2775 TRACE("Trying to overwrite locked entry\n");
2776 error = ERROR_SHARING_VIOLATION;
2780 hit_rate = pUrlEntry->dwHitRate;
2781 exempt_delta = pUrlEntry->dwExemptDelta;
2782 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2785 if (lpszLocalFileName)
2787 BOOL bFound = FALSE;
2789 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2791 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2792 error = ERROR_INVALID_PARAMETER;
2796 /* skip container path prefix */
2797 lpszLocalFileName += lstrlenW(pContainer->path);
2799 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2800 pchLocalFileName = achFile;
2802 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2804 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2813 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2814 error = ERROR_INVALID_PARAMETER;
2818 lpszLocalFileName += DIR_LENGTH + 1;
2819 pchLocalFileName += DIR_LENGTH + 1;
2822 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2823 if (lpszLocalFileName)
2825 dwOffsetLocalFileName = dwBytesNeeded;
2826 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2830 dwOffsetHeader = dwBytesNeeded;
2831 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2833 if (lpszFileExtensionA)
2835 dwOffsetFileExtension = dwBytesNeeded;
2836 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2839 /* round up to next block */
2840 if (dwBytesNeeded % BLOCKSIZE)
2842 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2843 dwBytesNeeded += BLOCKSIZE;
2846 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2847 while (error == ERROR_HANDLE_DISK_FULL)
2849 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2850 if (error == ERROR_SUCCESS)
2851 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2853 if (error != ERROR_SUCCESS)
2856 /* FindFirstFreeEntry fills in blocks used */
2857 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2858 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
2859 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2860 pUrlEntry->CacheDir = cDirectory;
2861 pUrlEntry->CacheEntryType = CacheEntryType;
2862 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2863 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
2865 /* Sticky entries have a default exempt time of one day */
2866 exempt_delta = 86400;
2868 pUrlEntry->dwExemptDelta = exempt_delta;
2869 pUrlEntry->dwHitRate = hit_rate+1;
2870 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2871 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2872 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2873 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2874 pUrlEntry->size.QuadPart = file_size.QuadPart;
2875 pUrlEntry->dwUseCount = 0;
2876 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2877 pUrlEntry->LastModifiedTime = LastModifiedTime;
2878 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2879 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2880 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2881 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2884 pUrlEntry->dwUnknown1 = 0;
2885 pUrlEntry->dwUnknown2 = 0;
2886 pUrlEntry->dwUnknown3 = 0x60;
2887 pUrlEntry->Unknown4 = 0;
2888 pUrlEntry->wUnknown5 = 0x1010;
2889 pUrlEntry->dwUnknown7 = 0;
2890 pUrlEntry->dwUnknown8 = 0;
2893 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2894 if (dwOffsetLocalFileName)
2895 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
2897 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2898 if (dwOffsetFileExtension)
2899 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2901 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
2902 while (error == ERROR_HANDLE_DISK_FULL)
2904 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2905 if (error == ERROR_SUCCESS)
2907 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
2908 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2909 url_entry_offset, HASHTABLE_URL);
2912 if (error != ERROR_SUCCESS)
2913 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2916 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2917 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2918 if (CacheEntryType & STICKY_CACHE_ENTRY)
2919 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2921 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2922 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2923 pHeader->CacheLimit.QuadPart)
2924 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2928 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2929 heap_free(lpszUrlNameA);
2930 heap_free(lpszFileExtensionA);
2932 if (error == ERROR_SUCCESS)
2936 SetLastError(error);
2941 /***********************************************************************
2942 * CommitUrlCacheEntryA (WININET.@)
2945 BOOL WINAPI CommitUrlCacheEntryA(
2946 IN LPCSTR lpszUrlName,
2947 IN LPCSTR lpszLocalFileName,
2948 IN FILETIME ExpireTime,
2949 IN FILETIME LastModifiedTime,
2950 IN DWORD CacheEntryType,
2951 IN LPBYTE lpHeaderInfo,
2952 IN DWORD dwHeaderSize,
2953 IN LPCSTR lpszFileExtension,
2954 IN LPCSTR lpszOriginalUrl
2957 WCHAR *url_name = NULL;
2958 WCHAR *local_file_name = NULL;
2959 WCHAR *original_url = NULL;
2960 WCHAR *file_extension = NULL;
2961 BOOL bSuccess = FALSE;
2963 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2964 debugstr_a(lpszUrlName),
2965 debugstr_a(lpszLocalFileName),
2969 debugstr_a(lpszFileExtension),
2970 debugstr_a(lpszOriginalUrl));
2972 url_name = heap_strdupAtoW(lpszUrlName);
2976 if (lpszLocalFileName)
2978 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2979 if (!local_file_name)
2982 if (lpszFileExtension)
2984 file_extension = heap_strdupAtoW(lpszFileExtension);
2985 if (!file_extension)
2988 if (lpszOriginalUrl)
2990 original_url = heap_strdupAtoW(lpszOriginalUrl);
2995 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2996 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2997 file_extension, original_url);
3000 heap_free(original_url);
3001 heap_free(file_extension);
3002 heap_free(local_file_name);
3003 heap_free(url_name);
3007 /***********************************************************************
3008 * CommitUrlCacheEntryW (WININET.@)
3011 BOOL WINAPI CommitUrlCacheEntryW(
3012 IN LPCWSTR lpszUrlName,
3013 IN LPCWSTR lpszLocalFileName,
3014 IN FILETIME ExpireTime,
3015 IN FILETIME LastModifiedTime,
3016 IN DWORD CacheEntryType,
3017 IN LPWSTR lpHeaderInfo,
3018 IN DWORD dwHeaderSize,
3019 IN LPCWSTR lpszFileExtension,
3020 IN LPCWSTR lpszOriginalUrl
3024 BOOL bSuccess = FALSE;
3026 CHAR *header_info = NULL;
3028 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3029 debugstr_w(lpszUrlName),
3030 debugstr_w(lpszLocalFileName),
3034 debugstr_w(lpszFileExtension),
3035 debugstr_w(lpszOriginalUrl));
3037 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3039 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3040 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3046 dwError = GetLastError();
3050 heap_free(header_info);
3052 SetLastError(dwError);
3058 /***********************************************************************
3059 * ReadUrlCacheEntryStream (WININET.@)
3062 BOOL WINAPI ReadUrlCacheEntryStream(
3063 IN HANDLE hUrlCacheStream,
3064 IN DWORD dwLocation,
3065 IN OUT LPVOID lpBuffer,
3066 IN OUT LPDWORD lpdwLen,
3070 /* Get handle to file from 'stream' */
3071 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3073 if (dwReserved != 0)
3075 ERR("dwReserved != 0\n");
3076 SetLastError(ERROR_INVALID_PARAMETER);
3080 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3082 SetLastError(ERROR_INVALID_HANDLE);
3086 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3088 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3091 /***********************************************************************
3092 * RetrieveUrlCacheEntryStreamA (WININET.@)
3095 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3096 IN LPCSTR lpszUrlName,
3097 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3098 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3099 IN BOOL fRandomRead,
3103 /* NOTE: this is not the same as the way that the native
3104 * version allocates 'stream' handles. I did it this way
3105 * as it is much easier and no applications should depend
3106 * on this behaviour. (Native version appears to allocate
3107 * indices into a table)
3109 STREAM_HANDLE * pStream;
3112 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3113 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3115 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3117 lpdwCacheEntryInfoBufferSize,
3123 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3128 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3130 if (hFile == INVALID_HANDLE_VALUE)
3133 /* allocate handle storage space */
3134 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3138 SetLastError(ERROR_OUTOFMEMORY);
3142 pStream->hFile = hFile;
3143 strcpy(pStream->lpszUrl, lpszUrlName);
3147 /***********************************************************************
3148 * RetrieveUrlCacheEntryStreamW (WININET.@)
3151 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3152 IN LPCWSTR lpszUrlName,
3153 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3154 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3155 IN BOOL fRandomRead,
3161 /* NOTE: this is not the same as the way that the native
3162 * version allocates 'stream' handles. I did it this way
3163 * as it is much easier and no applications should depend
3164 * on this behaviour. (Native version appears to allocate
3165 * indices into a table)
3167 STREAM_HANDLE * pStream;
3170 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3171 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3173 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3175 lpdwCacheEntryInfoBufferSize,
3181 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3186 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3188 if (hFile == INVALID_HANDLE_VALUE)
3191 /* allocate handle storage space */
3192 size = sizeof(STREAM_HANDLE);
3193 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3195 pStream = heap_alloc(size);
3199 SetLastError(ERROR_OUTOFMEMORY);
3203 pStream->hFile = hFile;
3204 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3208 /***********************************************************************
3209 * UnlockUrlCacheEntryStream (WININET.@)
3212 BOOL WINAPI UnlockUrlCacheEntryStream(
3213 IN HANDLE hUrlCacheStream,
3217 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3219 if (dwReserved != 0)
3221 ERR("dwReserved != 0\n");
3222 SetLastError(ERROR_INVALID_PARAMETER);
3226 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3228 SetLastError(ERROR_INVALID_HANDLE);
3232 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3235 CloseHandle(pStream->hFile);
3241 /***********************************************************************
3242 * DeleteUrlCacheEntryA (WININET.@)
3245 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3247 URLCACHECONTAINER * pContainer;
3248 LPURLCACHE_HEADER pHeader;
3249 struct _HASH_ENTRY * pHashEntry;
3253 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3255 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3256 if (error != ERROR_SUCCESS)
3258 SetLastError(error);
3262 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3263 if (error != ERROR_SUCCESS)
3265 SetLastError(error);
3269 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3272 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3274 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3275 TRACE("entry %s not found!\n", lpszUrlName);
3276 SetLastError(ERROR_FILE_NOT_FOUND);
3280 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3282 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3287 /***********************************************************************
3288 * DeleteUrlCacheEntryW (WININET.@)
3291 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3293 URLCACHECONTAINER * pContainer;
3294 LPURLCACHE_HEADER pHeader;
3295 struct _HASH_ENTRY * pHashEntry;
3300 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3302 urlA = heap_strdupWtoA(lpszUrlName);
3305 SetLastError(ERROR_OUTOFMEMORY);
3309 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3310 if (error != ERROR_SUCCESS)
3313 SetLastError(error);
3317 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3318 if (error != ERROR_SUCCESS)
3321 SetLastError(error);
3325 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3331 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3333 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3334 TRACE("entry %s not found!\n", debugstr_a(urlA));
3336 SetLastError(ERROR_FILE_NOT_FOUND);
3340 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3342 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3347 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3349 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3353 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3355 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3359 /***********************************************************************
3360 * CreateCacheContainerA (WININET.@)
3362 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3363 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3365 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3366 d1, d2, d3, d4, d5, d6, d7, d8);
3370 /***********************************************************************
3371 * CreateCacheContainerW (WININET.@)
3373 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3374 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3376 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3377 d1, d2, d3, d4, d5, d6, d7, d8);
3381 /***********************************************************************
3382 * FindFirstUrlCacheContainerA (WININET.@)
3384 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3386 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3390 /***********************************************************************
3391 * FindFirstUrlCacheContainerW (WININET.@)
3393 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3395 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3399 /***********************************************************************
3400 * FindNextUrlCacheContainerA (WININET.@)
3402 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3404 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3408 /***********************************************************************
3409 * FindNextUrlCacheContainerW (WININET.@)
3411 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3413 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3417 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3418 LPCSTR lpszUrlSearchPattern,
3422 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3423 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3425 LPDWORD pcbReserved2,
3429 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3430 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3431 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3432 SetLastError(ERROR_FILE_NOT_FOUND);
3436 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3437 LPCWSTR lpszUrlSearchPattern,
3441 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3442 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3444 LPDWORD pcbReserved2,
3448 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3449 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3450 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3451 SetLastError(ERROR_FILE_NOT_FOUND);
3455 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3457 typedef struct URLCacheFindEntryHandle
3460 LPWSTR lpszUrlSearchPattern;
3461 DWORD dwContainerIndex;
3462 DWORD dwHashTableIndex;
3463 DWORD dwHashEntryIndex;
3464 } URLCacheFindEntryHandle;
3466 /***********************************************************************
3467 * FindFirstUrlCacheEntryA (WININET.@)
3470 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3471 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3473 URLCacheFindEntryHandle *pEntryHandle;
3475 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3477 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3481 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3482 if (lpszUrlSearchPattern)
3484 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3485 if (!pEntryHandle->lpszUrlSearchPattern)
3487 heap_free(pEntryHandle);
3492 pEntryHandle->lpszUrlSearchPattern = NULL;
3493 pEntryHandle->dwContainerIndex = 0;
3494 pEntryHandle->dwHashTableIndex = 0;
3495 pEntryHandle->dwHashEntryIndex = 0;
3497 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3499 heap_free(pEntryHandle);
3502 return pEntryHandle;
3505 /***********************************************************************
3506 * FindFirstUrlCacheEntryW (WININET.@)
3509 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3510 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3512 URLCacheFindEntryHandle *pEntryHandle;
3514 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3516 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3520 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3521 if (lpszUrlSearchPattern)
3523 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3524 if (!pEntryHandle->lpszUrlSearchPattern)
3526 heap_free(pEntryHandle);
3531 pEntryHandle->lpszUrlSearchPattern = NULL;
3532 pEntryHandle->dwContainerIndex = 0;
3533 pEntryHandle->dwHashTableIndex = 0;
3534 pEntryHandle->dwHashEntryIndex = 0;
3536 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3538 heap_free(pEntryHandle);
3541 return pEntryHandle;
3544 static BOOL FindNextUrlCacheEntryInternal(
3546 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3547 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3550 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3551 URLCACHECONTAINER * pContainer;
3553 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3555 SetLastError(ERROR_INVALID_HANDLE);
3559 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3560 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3562 LPURLCACHE_HEADER pHeader;
3563 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3566 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3567 if (error != ERROR_SUCCESS)
3569 SetLastError(error);
3573 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3576 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3577 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3579 const struct _HASH_ENTRY *pHashEntry = NULL;
3580 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3581 pEntryHandle->dwHashEntryIndex++)
3583 const URL_CACHEFILE_ENTRY *pUrlEntry;
3584 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3586 if (pEntry->dwSignature != URL_SIGNATURE)
3589 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3590 TRACE("Found URL: %s\n",
3591 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3592 TRACE("Header info: %s\n",
3593 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3595 error = URLCache_CopyEntry(
3598 lpNextCacheEntryInfo,
3599 lpdwNextCacheEntryInfoBufferSize,
3602 if (error != ERROR_SUCCESS)
3604 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3605 SetLastError(error);
3608 TRACE("Local File Name: %s\n",
3609 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3611 /* increment the current index so that next time the function
3612 * is called the next entry is returned */
3613 pEntryHandle->dwHashEntryIndex++;
3614 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3619 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3622 SetLastError(ERROR_NO_MORE_ITEMS);
3626 /***********************************************************************
3627 * FindNextUrlCacheEntryA (WININET.@)
3629 BOOL WINAPI FindNextUrlCacheEntryA(
3631 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3632 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3634 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3636 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3637 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3640 /***********************************************************************
3641 * FindNextUrlCacheEntryW (WININET.@)
3643 BOOL WINAPI FindNextUrlCacheEntryW(
3645 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3646 LPDWORD lpdwNextCacheEntryInfoBufferSize
3649 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3651 return FindNextUrlCacheEntryInternal(hEnumHandle,
3652 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3653 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3656 /***********************************************************************
3657 * FindCloseUrlCache (WININET.@)
3659 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3661 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3663 TRACE("(%p)\n", hEnumHandle);
3665 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3667 SetLastError(ERROR_INVALID_HANDLE);
3671 pEntryHandle->dwMagic = 0;
3672 heap_free(pEntryHandle->lpszUrlSearchPattern);
3673 heap_free(pEntryHandle);
3677 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3678 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3680 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3681 dwSearchCondition, lpGroupId, lpReserved);
3685 BOOL WINAPI FindNextUrlCacheEntryExA(
3687 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3688 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3690 LPDWORD pcbReserved2,
3694 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3695 lpReserved, pcbReserved2, lpReserved3);
3699 BOOL WINAPI FindNextUrlCacheEntryExW(
3701 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3702 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3704 LPDWORD pcbReserved2,
3708 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3709 lpReserved, pcbReserved2, lpReserved3);
3713 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3715 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3719 /***********************************************************************
3720 * CreateUrlCacheGroup (WININET.@)
3723 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3725 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3729 /***********************************************************************
3730 * DeleteUrlCacheGroup (WININET.@)
3733 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3735 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3736 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3740 /***********************************************************************
3741 * SetUrlCacheEntryGroupA (WININET.@)
3744 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3745 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3748 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3749 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3750 pbGroupAttributes, cbGroupAttributes, lpReserved);
3751 SetLastError(ERROR_FILE_NOT_FOUND);
3755 /***********************************************************************
3756 * SetUrlCacheEntryGroupW (WININET.@)
3759 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3760 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3763 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3764 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3765 pbGroupAttributes, cbGroupAttributes, lpReserved);
3766 SetLastError(ERROR_FILE_NOT_FOUND);
3770 /***********************************************************************
3771 * GetUrlCacheConfigInfoW (WININET.@)
3773 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3775 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3776 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3780 /***********************************************************************
3781 * GetUrlCacheConfigInfoA (WININET.@)
3783 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3785 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3786 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3790 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3791 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3792 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3794 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3795 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3796 lpdwGroupInfo, lpReserved);
3800 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3801 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3802 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3804 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3805 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3806 lpdwGroupInfo, lpReserved);
3810 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3811 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3813 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3814 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3818 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3819 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3821 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3822 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3826 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3828 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3832 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3834 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3838 /***********************************************************************
3839 * DeleteIE3Cache (WININET.@)
3841 * Deletes the files used by the IE3 URL caching system.
3844 * hWnd [I] A dummy window.
3845 * hInst [I] Instance of process calling the function.
3846 * lpszCmdLine [I] Options used by function.
3847 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3849 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3851 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3855 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3856 FILETIME *pftLastModified)
3859 FILETIME now, expired;
3861 *pftLastModified = pUrlEntry->LastModifiedTime;
3862 GetSystemTimeAsFileTime(&now);
3863 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3864 pUrlEntry->wExpiredTime, &expired);
3865 /* If the expired time is 0, it's interpreted as not expired */
3866 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3869 ret = CompareFileTime(&expired, &now) < 0;
3873 /***********************************************************************
3874 * IsUrlCacheEntryExpiredA (WININET.@)
3878 * dwFlags [I] Unknown
3879 * pftLastModified [O] Last modified time
3881 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3883 LPURLCACHE_HEADER pHeader;
3884 struct _HASH_ENTRY * pHashEntry;
3885 const CACHEFILE_ENTRY * pEntry;
3886 const URL_CACHEFILE_ENTRY * pUrlEntry;
3887 URLCACHECONTAINER * pContainer;
3890 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3892 if (!url || !pftLastModified)
3895 FIXME("unknown flags 0x%08x\n", dwFlags);
3897 /* Any error implies that the URL is expired, i.e. not in the cache */
3898 if (URLCacheContainers_FindContainerA(url, &pContainer))
3900 memset(pftLastModified, 0, sizeof(*pftLastModified));
3904 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3906 memset(pftLastModified, 0, sizeof(*pftLastModified));
3910 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3912 memset(pftLastModified, 0, sizeof(*pftLastModified));
3916 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3918 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3919 memset(pftLastModified, 0, sizeof(*pftLastModified));
3920 TRACE("entry %s not found!\n", url);
3924 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3925 if (pEntry->dwSignature != URL_SIGNATURE)
3927 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3928 memset(pftLastModified, 0, sizeof(*pftLastModified));
3929 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3933 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3934 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3936 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3941 /***********************************************************************
3942 * IsUrlCacheEntryExpiredW (WININET.@)
3946 * dwFlags [I] Unknown
3947 * pftLastModified [O] Last modified time
3949 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3951 LPURLCACHE_HEADER pHeader;
3952 struct _HASH_ENTRY * pHashEntry;
3953 const CACHEFILE_ENTRY * pEntry;
3954 const URL_CACHEFILE_ENTRY * pUrlEntry;
3955 URLCACHECONTAINER * pContainer;
3958 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3960 if (!url || !pftLastModified)
3963 FIXME("unknown flags 0x%08x\n", dwFlags);
3965 /* Any error implies that the URL is expired, i.e. not in the cache */
3966 if (URLCacheContainers_FindContainerW(url, &pContainer))
3968 memset(pftLastModified, 0, sizeof(*pftLastModified));
3972 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3974 memset(pftLastModified, 0, sizeof(*pftLastModified));
3978 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3980 memset(pftLastModified, 0, sizeof(*pftLastModified));
3984 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3986 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3987 memset(pftLastModified, 0, sizeof(*pftLastModified));
3988 TRACE("entry %s not found!\n", debugstr_w(url));
3992 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3994 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3995 memset(pftLastModified, 0, sizeof(*pftLastModified));
3996 TRACE("entry %s not found!\n", debugstr_w(url));
4000 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4001 if (pEntry->dwSignature != URL_SIGNATURE)
4003 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4004 memset(pftLastModified, 0, sizeof(*pftLastModified));
4005 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4009 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4010 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4012 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4017 /***********************************************************************
4018 * GetDiskInfoA (WININET.@)
4020 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4023 ULARGE_INTEGER bytes_free, bytes_total;
4025 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4029 SetLastError(ERROR_INVALID_PARAMETER);
4033 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4035 if (cluster_size) *cluster_size = 1;
4036 if (free) *free = bytes_free.QuadPart;
4037 if (total) *total = bytes_total.QuadPart;
4042 /***********************************************************************
4043 * RegisterUrlCacheNotification (WININET.@)
4045 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4047 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4051 /***********************************************************************
4052 * IncrementUrlCacheHeaderData (WININET.@)
4054 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4056 FIXME("(%u, %p)\n", index, data);
4060 /***********************************************************************
4061 * RunOnceUrlCache (WININET.@)
4064 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4066 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);