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
65 #define MAX_DIR_NO 0x20
67 #define HASHTABLE_SIZE 448
68 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
69 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
70 #define ALLOCATION_TABLE_OFFSET 0x250
71 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
72 #define MIN_BLOCK_NO 0x80
73 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
74 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
76 #define HASHTABLE_URL 0
77 #define HASHTABLE_DEL 1
78 #define HASHTABLE_LOCK 2
79 #define HASHTABLE_FREE 3
80 #define HASHTABLE_REDR 5
81 #define HASHTABLE_FLAG_BITS 5
83 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
85 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
87 #define FILETIME_SECOND 10000000
89 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
90 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
91 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
92 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
93 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
95 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
97 typedef struct _CACHEFILE_ENTRY
101 DWORD dwSignature
; /* e.g. "URL " */
102 /* CHAR szSignature[4];
104 DWORD dwBlocksUsed
; /* number of 128byte blocks used by this entry */
107 typedef struct _URL_CACHEFILE_ENTRY
109 CACHEFILE_ENTRY CacheFileEntry
;
110 FILETIME LastModifiedTime
;
111 FILETIME LastAccessTime
;
112 WORD wExpiredDate
; /* expire date in dos format */
113 WORD wExpiredTime
; /* expire time in dos format */
114 DWORD dwUnknown1
; /* usually zero */
115 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
116 DWORD dwUnknown2
; /* usually zero */
117 DWORD dwExemptDelta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
118 DWORD dwUnknown3
; /* usually 0x60 */
119 DWORD dwOffsetUrl
; /* offset of start of url from start of entry */
120 BYTE CacheDir
; /* index of cache directory this url is stored in */
121 BYTE Unknown4
; /* usually zero */
122 WORD wUnknown5
; /* usually 0x1010 */
123 DWORD dwOffsetLocalName
; /* offset of start of local filename from start of entry */
124 DWORD CacheEntryType
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
125 DWORD dwOffsetHeaderInfo
; /* offset of start of header info from start of entry */
126 DWORD dwHeaderInfoSize
;
127 DWORD dwOffsetFileExtension
; /* offset of start of file extension from start of entry */
128 WORD wLastSyncDate
; /* last sync date in dos format */
129 WORD wLastSyncTime
; /* last sync time in dos format */
130 DWORD dwHitRate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
131 DWORD dwUseCount
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
134 DWORD dwUnknown7
; /* usually zero */
135 DWORD dwUnknown8
; /* usually zero */
136 /* packing to dword align start of next field */
137 /* CHAR szSourceUrlName[]; (url) */
138 /* packing to dword align start of next field */
139 /* CHAR szLocalFileName[]; (local file name excluding path) */
140 /* packing to dword align start of next field */
141 /* CHAR szHeaderInfo[]; (header info) */
142 } URL_CACHEFILE_ENTRY
;
150 typedef struct _HASH_CACHEFILE_ENTRY
152 CACHEFILE_ENTRY CacheFileEntry
;
154 DWORD dwHashTableNumber
;
155 struct _HASH_ENTRY HashTable
[HASHTABLE_SIZE
];
156 } HASH_CACHEFILE_ENTRY
;
158 typedef struct _DIRECTORY_DATA
161 char filename
[DIR_LENGTH
];
164 typedef struct _URLCACHE_HEADER
166 char szSignature
[28];
168 DWORD dwOffsetFirstHashTable
;
169 DWORD dwIndexCapacityInBlocks
;
172 ULARGE_INTEGER CacheLimit
;
173 ULARGE_INTEGER CacheUsage
;
174 ULARGE_INTEGER ExemptUsage
;
175 DWORD DirectoryCount
;
176 DIRECTORY_DATA directory_data
[MAX_DIR_NO
];
178 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
179 } URLCACHE_HEADER
, *LPURLCACHE_HEADER
;
180 typedef const URLCACHE_HEADER
*LPCURLCACHE_HEADER
;
182 typedef struct _STREAM_HANDLE
188 typedef struct _URLCACHECONTAINER
190 struct list entry
; /* part of a list */
191 LPWSTR cache_prefix
; /* string that has to be prefixed for this container to be used */
192 LPWSTR path
; /* path to url container directory */
193 HANDLE hMapping
; /* handle of file mapping */
194 DWORD file_size
; /* size of file when mapping was opened */
195 HANDLE hMutex
; /* handle of mutex */
196 DWORD default_entry_type
;
200 /* List of all containers available */
201 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
203 static DWORD
URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader
, HASH_CACHEFILE_ENTRY
*pPrevHash
, HASH_CACHEFILE_ENTRY
**ppHash
);
205 /***********************************************************************
206 * URLCache_PathToObjectName (Internal)
208 * Converts a path to a name suitable for use as a Win32 object name.
209 * Replaces '\\' characters in-place with the specified character
210 * (usually '_' or '!')
216 static void URLCache_PathToObjectName(LPWSTR lpszPath
, WCHAR replace
)
218 for (; *lpszPath
; lpszPath
++)
220 if (*lpszPath
== '\\')
225 /***********************************************************************
226 * URLCacheContainer_OpenIndex (Internal)
228 * Opens the index file and saves mapping handle in hCacheIndexMapping
231 * ERROR_SUCCESS if succeeded
232 * Any other Win32 error code if failed
235 static DWORD
URLCacheContainer_OpenIndex(URLCACHECONTAINER
* pContainer
, DWORD blocks_no
)
238 WCHAR wszFilePath
[MAX_PATH
];
239 DWORD dwFileSize
, new_file_size
;
241 static const WCHAR wszIndex
[] = {'i','n','d','e','x','.','d','a','t',0};
242 static const WCHAR wszMappingFormat
[] = {'%','s','%','s','_','%','l','u',0};
244 WaitForSingleObject(pContainer
->hMutex
, INFINITE
);
246 if (pContainer
->hMapping
) {
247 ReleaseMutex(pContainer
->hMutex
);
248 return ERROR_SUCCESS
;
251 strcpyW(wszFilePath
, pContainer
->path
);
252 strcatW(wszFilePath
, wszIndex
);
254 hFile
= CreateFileW(wszFilePath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
255 if (hFile
== INVALID_HANDLE_VALUE
)
257 /* Maybe the directory wasn't there? Try to create it */
258 if (CreateDirectoryW(pContainer
->path
, 0))
259 hFile
= CreateFileW(wszFilePath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
261 if (hFile
== INVALID_HANDLE_VALUE
)
263 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath
));
264 ReleaseMutex(pContainer
->hMutex
);
265 return GetLastError();
268 dwFileSize
= GetFileSize(hFile
, NULL
);
269 if (dwFileSize
== INVALID_FILE_SIZE
)
271 ReleaseMutex(pContainer
->hMutex
);
272 return GetLastError();
275 if (blocks_no
< MIN_BLOCK_NO
)
276 blocks_no
= MIN_BLOCK_NO
;
277 else if (blocks_no
> MAX_BLOCK_NO
)
278 blocks_no
= MAX_BLOCK_NO
;
279 new_file_size
= FILE_SIZE(blocks_no
);
281 if (dwFileSize
< new_file_size
)
283 static const CHAR szCacheContent
[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
285 char achZeroes
[0x1000];
287 DWORD dwError
= ERROR_SUCCESS
;
289 if (SetFilePointer(hFile
, 0, NULL
, FILE_END
) == INVALID_SET_FILE_POINTER
)
290 dwError
= GetLastError();
292 /* Write zeroes to the entire file so we can safely map it without
293 * fear of getting a SEGV because the disk is full.
295 memset(achZeroes
, 0, sizeof(achZeroes
));
296 for (dwOffset
= dwFileSize
; dwOffset
<new_file_size
&& dwError
==ERROR_SUCCESS
;
297 dwOffset
+= sizeof(achZeroes
))
299 DWORD dwWrite
= sizeof(achZeroes
);
302 if (new_file_size
- dwOffset
< dwWrite
)
303 dwWrite
= new_file_size
- dwOffset
;
304 if (!WriteFile(hFile
, achZeroes
, dwWrite
, &dwWritten
, 0) ||
305 dwWritten
!= dwWrite
)
307 /* If we fail to write, we need to return the error that
308 * cause the problem and also make sure the file is no
309 * longer there, if possible.
311 dwError
= GetLastError();
315 if (dwError
== ERROR_SUCCESS
)
317 HANDLE hMapping
= CreateFileMappingW(hFile
, NULL
, PAGE_READWRITE
, 0, 0, NULL
);
321 URLCACHE_HEADER
*pHeader
= MapViewOfFile(hMapping
, FILE_MAP_WRITE
, 0, 0, 0);
323 if (pHeader
&& dwFileSize
)
325 pHeader
->dwFileSize
= new_file_size
;
326 pHeader
->dwIndexCapacityInBlocks
= blocks_no
;
331 WCHAR wszDirPath
[MAX_PATH
];
334 HASH_CACHEFILE_ENTRY
*pHashEntry
;
336 /* First set some constants and defaults in the header */
337 strcpy(pHeader
->szSignature
, "WINE URLCache Ver 0.2005001");
338 pHeader
->dwFileSize
= new_file_size
;
339 pHeader
->dwIndexCapacityInBlocks
= blocks_no
;
340 /* 127MB - taken from default for Windows 2000 */
341 pHeader
->CacheLimit
.QuadPart
= 0x07ff5400;
342 /* Copied from a Windows 2000 cache index */
343 pHeader
->DirectoryCount
= 4;
345 /* If the registry has a cache size set, use the registry value */
346 if (RegOpenKeyA(HKEY_CURRENT_USER
, szCacheContent
, &key
) == ERROR_SUCCESS
)
349 DWORD len
= sizeof(dw
);
352 if (RegQueryValueExA(key
, "CacheLimit", NULL
, &keytype
,
353 (BYTE
*) &dw
, &len
) == ERROR_SUCCESS
&&
354 keytype
== REG_DWORD
)
356 pHeader
->CacheLimit
.QuadPart
= (ULONGLONG
)dw
* 1024;
361 URLCache_CreateHashTable(pHeader
, NULL
, &pHashEntry
);
363 /* Last step - create the directories */
365 strcpyW(wszDirPath
, pContainer
->path
);
366 pwchDir
= wszDirPath
+ strlenW(wszDirPath
);
369 GetSystemTimeAsFileTime(&ft
);
371 for (i
= 0; !dwError
&& i
< pHeader
->DirectoryCount
; ++i
)
373 pHeader
->directory_data
[i
].dwNumFiles
= 0;
377 ULONGLONG n
= ft
.dwHighDateTime
;
379 /* Generate a file name to attempt to create.
380 * This algorithm will create what will appear
381 * to be random and unrelated directory names
382 * of up to 9 characters in length.
385 n
+= ft
.dwLowDateTime
;
386 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
388 for (k
= 0; k
< 8; ++k
)
392 /* Dividing by a prime greater than 36 helps
393 * with the appearance of randomness
398 pwchDir
[k
] = '0' + r
;
400 pwchDir
[k
] = 'A' + (r
- 10);
403 if (CreateDirectoryW(wszDirPath
, 0))
405 /* The following is OK because we generated an
406 * 8 character directory name made from characters
407 * [A-Z0-9], which are equivalent for all code
408 * pages and for UTF-16
410 for (k
= 0; k
< 8; ++k
)
411 pHeader
->directory_data
[i
].filename
[k
] = pwchDir
[k
];
416 /* Give up. The most likely cause of this
417 * is a full disk, but whatever the cause
418 * is, it should be more than apparent that
421 dwError
= GetLastError();
427 UnmapViewOfFile(pHeader
);
431 dwError
= GetLastError();
433 dwFileSize
= new_file_size
;
434 CloseHandle(hMapping
);
438 dwError
= GetLastError();
445 DeleteFileW(wszFilePath
);
446 ReleaseMutex(pContainer
->hMutex
);
452 pContainer
->file_size
= dwFileSize
;
453 wsprintfW(wszFilePath
, wszMappingFormat
, pContainer
->path
, wszIndex
, dwFileSize
);
454 URLCache_PathToObjectName(wszFilePath
, '_');
455 pContainer
->hMapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, wszFilePath
);
456 if (!pContainer
->hMapping
)
457 pContainer
->hMapping
= CreateFileMappingW(hFile
, NULL
, PAGE_READWRITE
, 0, 0, wszFilePath
);
459 if (!pContainer
->hMapping
)
461 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
462 ReleaseMutex(pContainer
->hMutex
);
463 return GetLastError();
466 ReleaseMutex(pContainer
->hMutex
);
468 return ERROR_SUCCESS
;
471 /***********************************************************************
472 * URLCacheContainer_CloseIndex (Internal)
480 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER
* pContainer
)
482 CloseHandle(pContainer
->hMapping
);
483 pContainer
->hMapping
= NULL
;
486 static BOOL
URLCacheContainers_AddContainer(LPCWSTR cache_prefix
,
487 LPCWSTR path
, DWORD default_entry_type
, LPWSTR mutex_name
)
489 URLCACHECONTAINER
* pContainer
= heap_alloc(sizeof(URLCACHECONTAINER
));
490 int cache_prefix_len
= strlenW(cache_prefix
);
497 pContainer
->hMapping
= NULL
;
498 pContainer
->file_size
= 0;
499 pContainer
->default_entry_type
= default_entry_type
;
501 pContainer
->path
= heap_strdupW(path
);
502 if (!pContainer
->path
)
504 heap_free(pContainer
);
508 pContainer
->cache_prefix
= heap_alloc((cache_prefix_len
+ 1) * sizeof(WCHAR
));
509 if (!pContainer
->cache_prefix
)
511 heap_free(pContainer
->path
);
512 heap_free(pContainer
);
516 memcpy(pContainer
->cache_prefix
, cache_prefix
, (cache_prefix_len
+ 1) * sizeof(WCHAR
));
518 CharLowerW(mutex_name
);
519 URLCache_PathToObjectName(mutex_name
, '!');
521 if ((pContainer
->hMutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
523 ERR("couldn't create mutex (error is %d)\n", GetLastError());
524 heap_free(pContainer
->path
);
525 heap_free(pContainer
);
529 list_add_head(&UrlContainers
, &pContainer
->entry
);
534 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER
* pContainer
)
536 list_remove(&pContainer
->entry
);
538 URLCacheContainer_CloseIndex(pContainer
);
539 CloseHandle(pContainer
->hMutex
);
540 heap_free(pContainer
->path
);
541 heap_free(pContainer
->cache_prefix
);
542 heap_free(pContainer
);
545 static void URLCacheContainers_CreateDefaults(void)
547 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
548 static const WCHAR UrlPrefix
[] = {0};
549 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
550 static const WCHAR HistoryPrefix
[] = {'V','i','s','i','t','e','d',':',0};
551 static const WCHAR CookieSuffix
[] = {0};
552 static const WCHAR CookiePrefix
[] = {'C','o','o','k','i','e',':',0};
555 int nFolder
; /* CSIDL_* constant */
556 const WCHAR
* shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
557 const WCHAR
* cache_prefix
; /* prefix used to reference the container */
558 DWORD default_entry_type
;
559 } DefaultContainerData
[] =
561 { CSIDL_INTERNET_CACHE
, UrlSuffix
, UrlPrefix
, NORMAL_CACHE_ENTRY
},
562 { CSIDL_HISTORY
, HistorySuffix
, HistoryPrefix
, URLHISTORY_CACHE_ENTRY
},
563 { CSIDL_COOKIES
, CookieSuffix
, CookiePrefix
, COOKIE_CACHE_ENTRY
},
567 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
569 WCHAR wszCachePath
[MAX_PATH
];
570 WCHAR wszMutexName
[MAX_PATH
];
571 int path_len
, suffix_len
;
573 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
575 ERR("Couldn't get path for default container %u\n", i
);
578 path_len
= strlenW(wszCachePath
);
579 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
581 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
583 ERR("Path too long\n");
587 wszCachePath
[path_len
] = '\\';
588 wszCachePath
[path_len
+1] = 0;
590 strcpyW(wszMutexName
, wszCachePath
);
594 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
595 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
596 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
599 URLCacheContainers_AddContainer(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
600 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
604 static void URLCacheContainers_DeleteAll(void)
606 while(!list_empty(&UrlContainers
))
607 URLCacheContainer_DeleteContainer(
608 LIST_ENTRY(list_head(&UrlContainers
), URLCACHECONTAINER
, entry
)
612 static DWORD
URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl
, URLCACHECONTAINER
** ppContainer
)
614 URLCACHECONTAINER
* pContainer
;
616 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl
));
619 return ERROR_INVALID_PARAMETER
;
621 LIST_FOR_EACH_ENTRY(pContainer
, &UrlContainers
, URLCACHECONTAINER
, entry
)
623 int prefix_len
= strlenW(pContainer
->cache_prefix
);
624 if (!strncmpW(pContainer
->cache_prefix
, lpwszUrl
, prefix_len
))
626 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer
->cache_prefix
), debugstr_w(lpwszUrl
));
627 *ppContainer
= pContainer
;
628 return ERROR_SUCCESS
;
631 ERR("no container found\n");
632 return ERROR_FILE_NOT_FOUND
;
635 static DWORD
URLCacheContainers_FindContainerA(LPCSTR lpszUrl
, URLCACHECONTAINER
** ppContainer
)
640 if (lpszUrl
&& !(url
= heap_strdupAtoW(lpszUrl
)))
641 return ERROR_OUTOFMEMORY
;
643 ret
= URLCacheContainers_FindContainerW(url
, ppContainer
);
648 static BOOL
URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern
, DWORD dwIndex
, URLCACHECONTAINER
** ppContainer
)
651 URLCACHECONTAINER
* pContainer
;
653 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern
));
655 /* non-NULL search pattern only returns one container ever */
656 if (lpwszSearchPattern
&& dwIndex
> 0)
659 LIST_FOR_EACH_ENTRY(pContainer
, &UrlContainers
, URLCACHECONTAINER
, entry
)
661 if (lpwszSearchPattern
)
663 if (!strcmpW(pContainer
->cache_prefix
, lpwszSearchPattern
))
665 TRACE("found container with prefix %s\n", debugstr_w(pContainer
->cache_prefix
));
666 *ppContainer
= pContainer
;
674 TRACE("found container with prefix %s\n", debugstr_w(pContainer
->cache_prefix
));
675 *ppContainer
= pContainer
;
684 /***********************************************************************
685 * URLCacheContainer_LockIndex (Internal)
687 * Locks the index for system-wide exclusive access.
690 * Cache file header if successful
691 * NULL if failed and calls SetLastError.
693 static LPURLCACHE_HEADER
URLCacheContainer_LockIndex(URLCACHECONTAINER
* pContainer
)
697 URLCACHE_HEADER
* pHeader
;
701 WaitForSingleObject(pContainer
->hMutex
, INFINITE
);
703 pIndexData
= MapViewOfFile(pContainer
->hMapping
, FILE_MAP_WRITE
, 0, 0, 0);
707 ReleaseMutex(pContainer
->hMutex
);
708 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
711 pHeader
= (URLCACHE_HEADER
*)pIndexData
;
713 /* file has grown - we need to remap to prevent us getting
714 * access violations when we try and access beyond the end
715 * of the memory mapped file */
716 if (pHeader
->dwFileSize
!= pContainer
->file_size
)
718 UnmapViewOfFile( pHeader
);
719 URLCacheContainer_CloseIndex(pContainer
);
720 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
721 if (error
!= ERROR_SUCCESS
)
723 ReleaseMutex(pContainer
->hMutex
);
727 pIndexData
= MapViewOfFile(pContainer
->hMapping
, FILE_MAP_WRITE
, 0, 0, 0);
731 ReleaseMutex(pContainer
->hMutex
);
732 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
735 pHeader
= (URLCACHE_HEADER
*)pIndexData
;
738 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->szSignature
, pHeader
->dwFileSize
);
740 for (index
= 0; index
< pHeader
->DirectoryCount
; index
++)
742 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].filename
);
748 /***********************************************************************
749 * URLCacheContainer_UnlockIndex (Internal)
752 static BOOL
URLCacheContainer_UnlockIndex(URLCACHECONTAINER
* pContainer
, LPURLCACHE_HEADER pHeader
)
755 ReleaseMutex(pContainer
->hMutex
);
756 return UnmapViewOfFile(pHeader
);
760 #define CHAR_BIT (8 * sizeof(CHAR))
763 /***********************************************************************
764 * URLCache_Allocation_BlockIsFree (Internal)
766 * Is the specified block number free?
773 static inline BYTE
URLCache_Allocation_BlockIsFree(BYTE
* AllocationTable
, DWORD dwBlockNumber
)
775 BYTE mask
= 1 << (dwBlockNumber
% CHAR_BIT
);
776 return (AllocationTable
[dwBlockNumber
/ CHAR_BIT
] & mask
) == 0;
779 /***********************************************************************
780 * URLCache_Allocation_BlockFree (Internal)
782 * Marks the specified block as free
785 * this function is not updating used blocks count
791 static inline void URLCache_Allocation_BlockFree(BYTE
* AllocationTable
, DWORD dwBlockNumber
)
793 BYTE mask
= ~(1 << (dwBlockNumber
% CHAR_BIT
));
794 AllocationTable
[dwBlockNumber
/ CHAR_BIT
] &= mask
;
797 /***********************************************************************
798 * URLCache_Allocation_BlockAllocate (Internal)
800 * Marks the specified block as allocated
803 * this function is not updating used blocks count
809 static inline void URLCache_Allocation_BlockAllocate(BYTE
* AllocationTable
, DWORD dwBlockNumber
)
811 BYTE mask
= 1 << (dwBlockNumber
% CHAR_BIT
);
812 AllocationTable
[dwBlockNumber
/ CHAR_BIT
] |= mask
;
815 /***********************************************************************
816 * URLCache_FindFirstFreeEntry (Internal)
818 * Finds and allocates the first block of free space big enough and
819 * sets ppEntry to point to it.
822 * ERROR_SUCCESS when free memory block was found
823 * Any other Win32 error code if the entry could not be added
826 static DWORD
URLCache_FindFirstFreeEntry(URLCACHE_HEADER
* pHeader
, DWORD dwBlocksNeeded
, CACHEFILE_ENTRY
** ppEntry
)
830 for (dwBlockNumber
= 0; dwBlockNumber
< pHeader
->dwIndexCapacityInBlocks
; dwBlockNumber
++)
832 for (dwFreeCounter
= 0;
833 dwFreeCounter
< dwBlocksNeeded
&&
834 dwFreeCounter
+ dwBlockNumber
< pHeader
->dwIndexCapacityInBlocks
&&
835 URLCache_Allocation_BlockIsFree(pHeader
->allocation_table
, dwBlockNumber
+ dwFreeCounter
);
837 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber
, ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
839 if (dwFreeCounter
== dwBlocksNeeded
)
842 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber
, ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
843 for (index
= 0; index
< dwBlocksNeeded
; index
++)
844 URLCache_Allocation_BlockAllocate(pHeader
->allocation_table
, dwBlockNumber
+ index
);
845 *ppEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
846 for (index
= 0; index
< dwBlocksNeeded
* BLOCKSIZE
/ sizeof(DWORD
); index
++)
847 ((DWORD
*)*ppEntry
)[index
] = 0xdeadbeef;
848 (*ppEntry
)->dwBlocksUsed
= dwBlocksNeeded
;
849 pHeader
->dwBlocksInUse
+= dwBlocksNeeded
;
850 return ERROR_SUCCESS
;
854 return ERROR_HANDLE_DISK_FULL
;
857 /***********************************************************************
858 * URLCache_DeleteEntry (Internal)
860 * Deletes the specified entry and frees the space allocated to it
863 * TRUE if it succeeded
867 static BOOL
URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader
, CACHEFILE_ENTRY
* pEntry
)
872 /* update allocation table */
873 dwStartBlock
= ((DWORD
)((BYTE
*)pEntry
- (BYTE
*)pHeader
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
874 for (dwBlock
= dwStartBlock
; dwBlock
< dwStartBlock
+ pEntry
->dwBlocksUsed
; dwBlock
++)
875 URLCache_Allocation_BlockFree(pHeader
->allocation_table
, dwBlock
);
877 pHeader
->dwBlocksInUse
-= pEntry
->dwBlocksUsed
;
881 /***********************************************************************
882 * URLCache_LocalFileNameToPathW (Internal)
884 * Copies the full path to the specified buffer given the local file
885 * name and the index of the directory it is in. Always sets value in
886 * lpBufferSize to the required buffer size (in bytes).
889 * TRUE if the buffer was big enough
890 * FALSE if the buffer was too small
893 static BOOL
URLCache_LocalFileNameToPathW(
894 const URLCACHECONTAINER
* pContainer
,
895 LPCURLCACHE_HEADER pHeader
,
896 LPCSTR szLocalFileName
,
902 int path_len
= strlenW(pContainer
->path
);
903 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
904 if (Directory
>= pHeader
->DirectoryCount
)
910 nRequired
= (path_len
+ DIR_LENGTH
+ file_name_len
+ 1) * sizeof(WCHAR
);
911 if (nRequired
<= *lpBufferSize
)
915 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
916 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].filename
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
917 wszPath
[dir_len
+ path_len
] = '\\';
918 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
+ 1, file_name_len
);
919 *lpBufferSize
= nRequired
;
922 *lpBufferSize
= nRequired
;
926 /***********************************************************************
927 * URLCache_LocalFileNameToPathA (Internal)
929 * Copies the full path to the specified buffer given the local file
930 * name and the index of the directory it is in. Always sets value in
931 * lpBufferSize to the required buffer size.
934 * TRUE if the buffer was big enough
935 * FALSE if the buffer was too small
938 static BOOL
URLCache_LocalFileNameToPathA(
939 const URLCACHECONTAINER
* pContainer
,
940 LPCURLCACHE_HEADER pHeader
,
941 LPCSTR szLocalFileName
,
947 int path_len
, file_name_len
, dir_len
;
949 if (Directory
>= pHeader
->DirectoryCount
)
955 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
956 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
957 dir_len
= DIR_LENGTH
;
959 nRequired
= (path_len
+ dir_len
+ 1 + file_name_len
) * sizeof(char);
960 if (nRequired
< *lpBufferSize
)
962 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
963 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].filename
, dir_len
);
964 szPath
[path_len
+ dir_len
] = '\\';
965 memcpy(szPath
+ path_len
+ dir_len
+ 1, szLocalFileName
, file_name_len
);
966 *lpBufferSize
= nRequired
;
969 *lpBufferSize
= nRequired
;
973 /* Just like FileTimeToDosDateTime, except that it also maps the special
974 * case of a filetime of (0,0) to a DOS date/time of (0,0).
976 static void URLCache_FileTimeToDosDateTime(const FILETIME
*ft
, WORD
*fatdate
,
979 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
980 *fatdate
= *fattime
= 0;
982 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
985 /***********************************************************************
986 * URLCache_DeleteFile (Internal)
988 static DWORD
URLCache_DeleteFile(const URLCACHECONTAINER
*container
,
989 URLCACHE_HEADER
*header
, URL_CACHEFILE_ENTRY
*url_entry
)
991 WIN32_FILE_ATTRIBUTE_DATA attr
;
992 WCHAR path
[MAX_PATH
];
993 LONG path_size
= sizeof(path
);
997 if(!url_entry
->dwOffsetLocalName
)
1000 if(!URLCache_LocalFileNameToPathW(container
, header
,
1001 (LPCSTR
)url_entry
+url_entry
->dwOffsetLocalName
,
1002 url_entry
->CacheDir
, path
, &path_size
))
1005 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1007 URLCache_FileTimeToDosDateTime(&attr
.ftLastWriteTime
, &date
, &time
);
1008 if(date
!= url_entry
->LastWriteDate
|| time
!= url_entry
->LastWriteTime
)
1011 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1012 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1016 if (url_entry
->CacheDir
< header
->DirectoryCount
)
1018 if (header
->directory_data
[url_entry
->CacheDir
].dwNumFiles
)
1019 header
->directory_data
[url_entry
->CacheDir
].dwNumFiles
--;
1021 if (url_entry
->CacheEntryType
& STICKY_CACHE_ENTRY
)
1023 if (url_entry
->size
.QuadPart
< header
->ExemptUsage
.QuadPart
)
1024 header
->ExemptUsage
.QuadPart
-= url_entry
->size
.QuadPart
;
1026 header
->ExemptUsage
.QuadPart
= 0;
1030 if (url_entry
->size
.QuadPart
< header
->CacheUsage
.QuadPart
)
1031 header
->CacheUsage
.QuadPart
-= url_entry
->size
.QuadPart
;
1033 header
->CacheUsage
.QuadPart
= 0;
1036 return ERROR_SUCCESS
;
1039 static BOOL
urlcache_clean_leaked_entries(URLCACHECONTAINER
*container
, URLCACHE_HEADER
*header
)
1044 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1046 URL_CACHEFILE_ENTRY
*url_entry
= (URL_CACHEFILE_ENTRY
*)((LPBYTE
)header
+ *leak_off
);
1048 if(SUCCEEDED(URLCache_DeleteFile(container
, header
, url_entry
))) {
1049 *leak_off
= url_entry
->dwExemptDelta
;
1050 URLCache_DeleteEntry(header
, &url_entry
->CacheFileEntry
);
1053 leak_off
= &url_entry
->dwExemptDelta
;
1060 /***********************************************************************
1061 * URLCacheContainer_CleanIndex (Internal)
1063 * This function is meant to make place in index file by removing leaked
1064 * files entries and resizing the file.
1066 * CAUTION: file view may get mapped to new memory
1069 * ERROR_SUCCESS when new memory is available
1070 * error code otherwise
1072 static DWORD
URLCacheContainer_CleanIndex(URLCACHECONTAINER
*container
, URLCACHE_HEADER
**file_view
)
1074 URLCACHE_HEADER
*header
= *file_view
;
1077 TRACE("(%s %s)\n", debugstr_w(container
->cache_prefix
), debugstr_w(container
->path
));
1079 if(urlcache_clean_leaked_entries(container
, header
))
1080 return ERROR_SUCCESS
;
1082 if(header
->dwFileSize
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1083 WARN("index file has maximal size\n");
1084 return ERROR_NOT_ENOUGH_MEMORY
;
1087 URLCacheContainer_CloseIndex(container
);
1088 ret
= URLCacheContainer_OpenIndex(container
, header
->dwIndexCapacityInBlocks
*2);
1089 if(ret
!= ERROR_SUCCESS
)
1091 header
= MapViewOfFile(container
->hMapping
, FILE_MAP_WRITE
, 0, 0, 0);
1093 return GetLastError();
1095 UnmapViewOfFile(*file_view
);
1096 *file_view
= header
;
1097 return ERROR_SUCCESS
;
1100 /* Just like DosDateTimeToFileTime, except that it also maps the special
1101 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1103 static void URLCache_DosDateTimeToFileTime(WORD fatdate
, WORD fattime
,
1106 if (!fatdate
&& !fattime
)
1107 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1109 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1112 /***********************************************************************
1113 * URLCache_CopyEntry (Internal)
1115 * Copies an entry from the cache index file to the Win32 structure
1118 * ERROR_SUCCESS if the buffer was big enough
1119 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1122 static DWORD
URLCache_CopyEntry(
1123 URLCACHECONTAINER
* pContainer
,
1124 LPCURLCACHE_HEADER pHeader
,
1125 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1126 LPDWORD lpdwBufferSize
,
1127 const URL_CACHEFILE_ENTRY
* pUrlEntry
,
1131 DWORD dwRequiredSize
= sizeof(*lpCacheEntryInfo
);
1133 if (*lpdwBufferSize
>= dwRequiredSize
)
1135 lpCacheEntryInfo
->lpHeaderInfo
= NULL
;
1136 lpCacheEntryInfo
->lpszFileExtension
= NULL
;
1137 lpCacheEntryInfo
->lpszLocalFileName
= NULL
;
1138 lpCacheEntryInfo
->lpszSourceUrlName
= NULL
;
1139 lpCacheEntryInfo
->CacheEntryType
= pUrlEntry
->CacheEntryType
;
1140 lpCacheEntryInfo
->u
.dwExemptDelta
= pUrlEntry
->dwExemptDelta
;
1141 lpCacheEntryInfo
->dwHeaderInfoSize
= pUrlEntry
->dwHeaderInfoSize
;
1142 lpCacheEntryInfo
->dwHitRate
= pUrlEntry
->dwHitRate
;
1143 lpCacheEntryInfo
->dwSizeHigh
= pUrlEntry
->size
.u
.HighPart
;
1144 lpCacheEntryInfo
->dwSizeLow
= pUrlEntry
->size
.u
.LowPart
;
1145 lpCacheEntryInfo
->dwStructSize
= sizeof(*lpCacheEntryInfo
);
1146 lpCacheEntryInfo
->dwUseCount
= pUrlEntry
->dwUseCount
;
1147 URLCache_DosDateTimeToFileTime(pUrlEntry
->wExpiredDate
, pUrlEntry
->wExpiredTime
, &lpCacheEntryInfo
->ExpireTime
);
1148 lpCacheEntryInfo
->LastAccessTime
.dwHighDateTime
= pUrlEntry
->LastAccessTime
.dwHighDateTime
;
1149 lpCacheEntryInfo
->LastAccessTime
.dwLowDateTime
= pUrlEntry
->LastAccessTime
.dwLowDateTime
;
1150 lpCacheEntryInfo
->LastModifiedTime
.dwHighDateTime
= pUrlEntry
->LastModifiedTime
.dwHighDateTime
;
1151 lpCacheEntryInfo
->LastModifiedTime
.dwLowDateTime
= pUrlEntry
->LastModifiedTime
.dwLowDateTime
;
1152 URLCache_DosDateTimeToFileTime(pUrlEntry
->wLastSyncDate
, pUrlEntry
->wLastSyncTime
, &lpCacheEntryInfo
->LastSyncTime
);
1155 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1156 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1157 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1159 lenUrl
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
, -1, NULL
, 0);
1161 lenUrl
= strlen((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
);
1162 dwRequiredSize
+= (lenUrl
+ 1) * (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1164 /* FIXME: is source url optional? */
1165 if (*lpdwBufferSize
>= dwRequiredSize
)
1167 DWORD lenUrlBytes
= (lenUrl
+1) * (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1169 lpCacheEntryInfo
->lpszSourceUrlName
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
- lenUrlBytes
;
1171 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
, -1, (LPWSTR
)lpCacheEntryInfo
->lpszSourceUrlName
, lenUrl
+ 1);
1173 memcpy(lpCacheEntryInfo
->lpszSourceUrlName
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
, lenUrlBytes
);
1176 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1177 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1178 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1180 if (pUrlEntry
->dwOffsetLocalName
)
1182 LONG nLocalFilePathSize
;
1183 LPSTR lpszLocalFileName
;
1184 lpszLocalFileName
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
;
1185 nLocalFilePathSize
= *lpdwBufferSize
- dwRequiredSize
;
1186 if ((bUnicode
&& URLCache_LocalFileNameToPathW(pContainer
, pHeader
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
, pUrlEntry
->CacheDir
, (LPWSTR
)lpszLocalFileName
, &nLocalFilePathSize
)) ||
1187 (!bUnicode
&& URLCache_LocalFileNameToPathA(pContainer
, pHeader
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
, pUrlEntry
->CacheDir
, lpszLocalFileName
, &nLocalFilePathSize
)))
1189 lpCacheEntryInfo
->lpszLocalFileName
= lpszLocalFileName
;
1191 dwRequiredSize
+= nLocalFilePathSize
* (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) ;
1193 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1194 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1195 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1197 dwRequiredSize
+= pUrlEntry
->dwHeaderInfoSize
+ 1;
1199 if (*lpdwBufferSize
>= dwRequiredSize
)
1201 lpCacheEntryInfo
->lpHeaderInfo
= (LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
- pUrlEntry
->dwHeaderInfoSize
- 1;
1202 memcpy(lpCacheEntryInfo
->lpHeaderInfo
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
, pUrlEntry
->dwHeaderInfoSize
);
1203 ((LPBYTE
)lpCacheEntryInfo
)[dwRequiredSize
- 1] = '\0';
1205 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1206 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1207 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1209 if (pUrlEntry
->dwOffsetFileExtension
)
1214 lenExtension
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetFileExtension
, -1, NULL
, 0);
1216 lenExtension
= strlen((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetFileExtension
) + 1;
1217 dwRequiredSize
+= lenExtension
* (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1219 if (*lpdwBufferSize
>= dwRequiredSize
)
1221 lpCacheEntryInfo
->lpszFileExtension
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
- lenExtension
;
1223 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetFileExtension
, -1, (LPWSTR
)lpCacheEntryInfo
->lpszSourceUrlName
, lenExtension
);
1225 memcpy(lpCacheEntryInfo
->lpszFileExtension
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetFileExtension
, lenExtension
* sizeof(CHAR
));
1228 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1229 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1230 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1233 if (dwRequiredSize
> *lpdwBufferSize
)
1235 *lpdwBufferSize
= dwRequiredSize
;
1236 return ERROR_INSUFFICIENT_BUFFER
;
1238 *lpdwBufferSize
= dwRequiredSize
;
1239 return ERROR_SUCCESS
;
1242 /***********************************************************************
1243 * URLCache_SetEntryInfo (Internal)
1245 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1246 * according to the flags set by dwFieldControl.
1249 * ERROR_SUCCESS if the buffer was big enough
1250 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1253 static DWORD
URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY
* pUrlEntry
, const INTERNET_CACHE_ENTRY_INFOW
* lpCacheEntryInfo
, DWORD dwFieldControl
)
1255 if (dwFieldControl
& CACHE_ENTRY_ACCTIME_FC
)
1256 pUrlEntry
->LastAccessTime
= lpCacheEntryInfo
->LastAccessTime
;
1257 if (dwFieldControl
& CACHE_ENTRY_ATTRIBUTE_FC
)
1258 pUrlEntry
->CacheEntryType
= lpCacheEntryInfo
->CacheEntryType
;
1259 if (dwFieldControl
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1260 pUrlEntry
->dwExemptDelta
= lpCacheEntryInfo
->u
.dwExemptDelta
;
1261 if (dwFieldControl
& CACHE_ENTRY_EXPTIME_FC
)
1262 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo
->ExpireTime
, &pUrlEntry
->wExpiredDate
, &pUrlEntry
->wExpiredTime
);
1263 if (dwFieldControl
& CACHE_ENTRY_HEADERINFO_FC
)
1264 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1265 if (dwFieldControl
& CACHE_ENTRY_HITRATE_FC
)
1266 pUrlEntry
->dwHitRate
= lpCacheEntryInfo
->dwHitRate
;
1267 if (dwFieldControl
& CACHE_ENTRY_MODTIME_FC
)
1268 pUrlEntry
->LastModifiedTime
= lpCacheEntryInfo
->LastModifiedTime
;
1269 if (dwFieldControl
& CACHE_ENTRY_SYNCTIME_FC
)
1270 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo
->LastAccessTime
, &pUrlEntry
->wLastSyncDate
, &pUrlEntry
->wLastSyncTime
);
1272 return ERROR_SUCCESS
;
1275 /***********************************************************************
1276 * URLCache_HashKey (Internal)
1278 * Returns the hash key for a given string
1281 * hash key for the string
1284 static DWORD
URLCache_HashKey(LPCSTR lpszKey
)
1286 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1287 * but the algorithm and result are not the same!
1289 static const unsigned char lookupTable
[256] =
1291 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1292 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1293 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1294 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1295 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1296 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1297 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1298 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1299 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1300 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1301 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1302 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1303 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1304 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1305 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1306 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1307 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1308 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1309 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1310 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1311 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1312 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1313 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1314 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1315 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1316 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1317 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1318 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1319 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1320 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1321 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1322 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1327 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1328 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1330 for (lpszKey
++; *lpszKey
&& ((lpszKey
[0] != '/') || (lpszKey
[1] != 0)); lpszKey
++)
1332 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1333 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1336 return *(DWORD
*)key
;
1339 static inline HASH_CACHEFILE_ENTRY
* URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader
, DWORD dwOffset
)
1341 return (HASH_CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ dwOffset
);
1344 static inline BOOL
URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader
, const HASH_CACHEFILE_ENTRY
*pHashEntry
)
1346 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1347 return ((DWORD
)((const BYTE
*)pHashEntry
- (const BYTE
*)pHeader
) >= ENTRY_START_OFFSET
) &&
1348 ((DWORD
)((const BYTE
*)pHashEntry
- (const BYTE
*)pHeader
) < pHeader
->dwFileSize
);
1351 static BOOL
URLCache_FindHash(LPCURLCACHE_HEADER pHeader
, LPCSTR lpszUrl
, struct _HASH_ENTRY
** ppHashEntry
)
1353 /* structure of hash table:
1354 * 448 entries divided into 64 blocks
1355 * each block therefore contains a chain of 7 key/offset pairs
1356 * how position in table is calculated:
1357 * 1. the url is hashed in helper function
1358 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1359 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1362 * there can be multiple hash tables in the file and the offset to
1363 * the next one is stored in the header of the hash table
1365 DWORD key
= URLCache_HashKey(lpszUrl
);
1366 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1367 HASH_CACHEFILE_ENTRY
* pHashEntry
;
1368 DWORD dwHashTableNumber
= 0;
1370 key
>>= HASHTABLE_FLAG_BITS
;
1372 for (pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHeader
->dwOffsetFirstHashTable
);
1373 URLCache_IsHashEntryValid(pHeader
, pHashEntry
);
1374 pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHashEntry
->dwAddressNext
))
1377 if (pHashEntry
->dwHashTableNumber
!= dwHashTableNumber
++)
1379 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->dwHashTableNumber
, dwHashTableNumber
);
1382 /* make sure that it is in fact a hash entry */
1383 if (pHashEntry
->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
)
1385 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->CacheFileEntry
.dwSignature
);
1389 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1391 struct _HASH_ENTRY
* pHashElement
= &pHashEntry
->HashTable
[offset
+ i
];
1392 if (key
== pHashElement
->dwHashKey
>>HASHTABLE_FLAG_BITS
)
1394 /* FIXME: we should make sure that this is the right element
1395 * before returning and claiming that it is. We can do this
1396 * by doing a simple compare between the URL we were given
1397 * and the URL stored in the entry. However, this assumes
1398 * we know the format of all the entries stored in the
1400 *ppHashEntry
= pHashElement
;
1408 static BOOL
URLCache_FindHashW(LPCURLCACHE_HEADER pHeader
, LPCWSTR lpszUrl
, struct _HASH_ENTRY
** ppHashEntry
)
1413 urlA
= heap_strdupWtoA(lpszUrl
);
1416 SetLastError(ERROR_OUTOFMEMORY
);
1420 ret
= URLCache_FindHash(pHeader
, urlA
, ppHashEntry
);
1425 /***********************************************************************
1426 * URLCache_HashEntrySetFlags (Internal)
1428 * Sets special bits in hash key
1434 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY
* pHashEntry
, DWORD dwFlag
)
1436 pHashEntry
->dwHashKey
= (pHashEntry
->dwHashKey
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1439 /***********************************************************************
1440 * URLCache_DeleteEntryFromHash (Internal)
1442 * Searches all the hash tables in the index for the given URL and
1443 * then if found deletes the entry.
1446 * TRUE if the entry was found
1447 * FALSE if the entry could not be found
1450 static BOOL
URLCache_DeleteEntryFromHash(struct _HASH_ENTRY
* pHashEntry
)
1452 pHashEntry
->dwHashKey
= HASHTABLE_DEL
;
1456 /***********************************************************************
1457 * URLCache_AddEntryToHash (Internal)
1459 * Searches all the hash tables for a free slot based on the offset
1460 * generated from the hash key. If a free slot is found, the offset and
1461 * key are entered into the hash table.
1464 * ERROR_SUCCESS if the entry was added
1465 * Any other Win32 error code if the entry could not be added
1468 static DWORD
URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1470 /* see URLCache_FindEntryInHash for structure of hash tables */
1472 DWORD key
= URLCache_HashKey(lpszUrl
);
1473 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1474 HASH_CACHEFILE_ENTRY
* pHashEntry
, *pHashPrev
= NULL
;
1475 DWORD dwHashTableNumber
= 0;
1478 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1480 for (pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHeader
->dwOffsetFirstHashTable
);
1481 URLCache_IsHashEntryValid(pHeader
, pHashEntry
);
1482 pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHashEntry
->dwAddressNext
))
1485 pHashPrev
= pHashEntry
;
1487 if (pHashEntry
->dwHashTableNumber
!= dwHashTableNumber
++)
1489 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->dwHashTableNumber
, dwHashTableNumber
);
1492 /* make sure that it is in fact a hash entry */
1493 if (pHashEntry
->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
)
1495 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->CacheFileEntry
.dwSignature
);
1499 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1501 struct _HASH_ENTRY
* pHashElement
= &pHashEntry
->HashTable
[offset
+ i
];
1502 if (pHashElement
->dwHashKey
==HASHTABLE_FREE
|| pHashElement
->dwHashKey
==HASHTABLE_DEL
) /* if the slot is free */
1504 pHashElement
->dwHashKey
= key
;
1505 pHashElement
->dwOffsetEntry
= dwOffsetEntry
;
1506 return ERROR_SUCCESS
;
1510 error
= URLCache_CreateHashTable(pHeader
, pHashPrev
, &pHashEntry
);
1511 if (error
!= ERROR_SUCCESS
)
1514 pHashEntry
->HashTable
[offset
].dwHashKey
= key
;
1515 pHashEntry
->HashTable
[offset
].dwOffsetEntry
= dwOffsetEntry
;
1516 return ERROR_SUCCESS
;
1519 /***********************************************************************
1520 * URLCache_CreateHashTable (Internal)
1522 * Creates a new hash table in free space and adds it to the chain of existing
1526 * ERROR_SUCCESS if the hash table was created
1527 * ERROR_DISK_FULL if the hash table could not be created
1530 static DWORD
URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader
, HASH_CACHEFILE_ENTRY
*pPrevHash
, HASH_CACHEFILE_ENTRY
**ppHash
)
1532 DWORD dwOffset
, error
;
1535 if ((error
= URLCache_FindFirstFreeEntry(pHeader
, 0x20, (CACHEFILE_ENTRY
**)ppHash
)) != ERROR_SUCCESS
)
1538 dwOffset
= (BYTE
*)*ppHash
- (BYTE
*)pHeader
;
1541 pPrevHash
->dwAddressNext
= dwOffset
;
1543 pHeader
->dwOffsetFirstHashTable
= dwOffset
;
1544 (*ppHash
)->CacheFileEntry
.dwSignature
= HASH_SIGNATURE
;
1545 (*ppHash
)->CacheFileEntry
.dwBlocksUsed
= 0x20;
1546 (*ppHash
)->dwAddressNext
= 0;
1547 (*ppHash
)->dwHashTableNumber
= pPrevHash
? pPrevHash
->dwHashTableNumber
+ 1 : 0;
1548 for (i
= 0; i
< HASHTABLE_SIZE
; i
++)
1550 (*ppHash
)->HashTable
[i
].dwOffsetEntry
= HASHTABLE_FREE
;
1551 (*ppHash
)->HashTable
[i
].dwHashKey
= HASHTABLE_FREE
;
1553 return ERROR_SUCCESS
;
1556 /***********************************************************************
1557 * URLCache_EnumHashTables (Internal)
1559 * Enumerates the hash tables in a container.
1562 * TRUE if an entry was found
1563 * FALSE if there are no more tables to enumerate.
1566 static BOOL
URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader
, DWORD
*pdwHashTableNumber
, HASH_CACHEFILE_ENTRY
** ppHashEntry
)
1568 for (*ppHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHeader
->dwOffsetFirstHashTable
);
1569 URLCache_IsHashEntryValid(pHeader
, *ppHashEntry
);
1570 *ppHashEntry
= URLCache_HashEntryFromOffset(pHeader
, (*ppHashEntry
)->dwAddressNext
))
1572 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->dwHashTableNumber
);
1573 if ((*ppHashEntry
)->dwHashTableNumber
!= *pdwHashTableNumber
)
1575 /* make sure that it is in fact a hash entry */
1576 if ((*ppHashEntry
)->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
)
1578 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->CacheFileEntry
.dwSignature
);
1579 (*pdwHashTableNumber
)++;
1583 TRACE("hash table number %d found\n", *pdwHashTableNumber
);
1589 /***********************************************************************
1590 * URLCache_EnumHashTableEntries (Internal)
1592 * Enumerates entries in a hash table and returns the next non-free entry.
1595 * TRUE if an entry was found
1596 * FALSE if the hash table is empty or there are no more entries to
1600 static BOOL
URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader
, const HASH_CACHEFILE_ENTRY
* pHashEntry
,
1601 DWORD
* index
, const struct _HASH_ENTRY
** ppHashEntry
)
1603 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1605 if (pHashEntry
->HashTable
[*index
].dwHashKey
==HASHTABLE_FREE
|| pHashEntry
->HashTable
[*index
].dwHashKey
==HASHTABLE_DEL
)
1608 *ppHashEntry
= &pHashEntry
->HashTable
[*index
];
1609 TRACE("entry found %d\n", *index
);
1612 TRACE("no more entries (%d)\n", *index
);
1616 /***********************************************************************
1617 * URLCache_DeleteCacheDirectory (Internal)
1619 * Erase a directory containing an URL cache.
1622 * TRUE success, FALSE failure/aborted.
1625 static BOOL
URLCache_DeleteCacheDirectory(LPCWSTR lpszPath
)
1628 WCHAR path
[MAX_PATH
+ 1];
1629 SHFILEOPSTRUCTW shfos
;
1632 path_len
= strlenW(lpszPath
);
1633 if (path_len
>= MAX_PATH
)
1635 strcpyW(path
, lpszPath
);
1636 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1639 shfos
.wFunc
= FO_DELETE
;
1643 shfos
.fAnyOperationsAborted
= FALSE
;
1644 ret
= SHFileOperationW(&shfos
);
1646 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1647 return !(ret
|| shfos
.fAnyOperationsAborted
);
1650 /***********************************************************************
1651 * URLCache_IsLocked (Internal)
1653 * Checks if entry is locked. Unlocks it if possible.
1655 static BOOL
URLCache_IsLocked(struct _HASH_ENTRY
*hash_entry
, URL_CACHEFILE_ENTRY
*url_entry
)
1658 ULARGE_INTEGER acc_time
, time
;
1660 if ((hash_entry
->dwHashKey
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1663 GetSystemTimeAsFileTime(&cur_time
);
1664 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1665 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1667 acc_time
.u
.LowPart
= url_entry
->LastAccessTime
.dwLowDateTime
;
1668 acc_time
.u
.HighPart
= url_entry
->LastAccessTime
.dwHighDateTime
;
1670 time
.QuadPart
-= acc_time
.QuadPart
;
1672 /* check if entry was locked for at least a day */
1673 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1674 URLCache_HashEntrySetFlags(hash_entry
, HASHTABLE_URL
);
1675 url_entry
->dwUseCount
= 0;
1682 /***********************************************************************
1683 * GetUrlCacheEntryInfoExA (WININET.@)
1686 BOOL WINAPI
GetUrlCacheEntryInfoExA(
1688 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1689 LPDWORD lpdwCacheEntryInfoBufSize
,
1691 LPDWORD lpdwReserved
,
1695 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1696 debugstr_a(lpszUrl
),
1698 lpdwCacheEntryInfoBufSize
,
1704 if ((lpszReserved
!= NULL
) ||
1705 (lpdwReserved
!= NULL
) ||
1706 (lpReserved
!= NULL
))
1708 ERR("Reserved value was not 0\n");
1709 SetLastError(ERROR_INVALID_PARAMETER
);
1714 FIXME("Undocumented flag(s): %x\n", dwFlags
);
1715 SetLastError(ERROR_FILE_NOT_FOUND
);
1718 return GetUrlCacheEntryInfoA(lpszUrl
, lpCacheEntryInfo
, lpdwCacheEntryInfoBufSize
);
1721 /***********************************************************************
1722 * GetUrlCacheEntryInfoA (WININET.@)
1725 BOOL WINAPI
GetUrlCacheEntryInfoA(
1726 IN LPCSTR lpszUrlName
,
1727 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1728 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1731 LPURLCACHE_HEADER pHeader
;
1732 struct _HASH_ENTRY
* pHashEntry
;
1733 const CACHEFILE_ENTRY
* pEntry
;
1734 const URL_CACHEFILE_ENTRY
* pUrlEntry
;
1735 URLCACHECONTAINER
* pContainer
;
1738 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, lpdwCacheEntryInfoBufferSize
);
1740 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
1741 if (error
!= ERROR_SUCCESS
)
1743 SetLastError(error
);
1747 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
1748 if (error
!= ERROR_SUCCESS
)
1750 SetLastError(error
);
1754 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
1757 if (!URLCache_FindHash(pHeader
, lpszUrlName
, &pHashEntry
))
1759 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1760 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
1761 SetLastError(ERROR_FILE_NOT_FOUND
);
1765 pEntry
= (const CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
1766 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
1768 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1769 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
1770 SetLastError(ERROR_FILE_NOT_FOUND
);
1774 pUrlEntry
= (const URL_CACHEFILE_ENTRY
*)pEntry
;
1775 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
));
1776 if (pUrlEntry
->dwOffsetHeaderInfo
)
1777 TRACE("Header info: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
));
1779 if (lpdwCacheEntryInfoBufferSize
)
1781 if (!lpCacheEntryInfo
)
1782 *lpdwCacheEntryInfoBufferSize
= 0;
1784 error
= URLCache_CopyEntry(
1788 lpdwCacheEntryInfoBufferSize
,
1791 if (error
!= ERROR_SUCCESS
)
1793 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1794 SetLastError(error
);
1797 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
));
1800 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1805 /***********************************************************************
1806 * GetUrlCacheEntryInfoW (WININET.@)
1809 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1810 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1811 LPDWORD lpdwCacheEntryInfoBufferSize
)
1813 LPURLCACHE_HEADER pHeader
;
1814 struct _HASH_ENTRY
* pHashEntry
;
1815 const CACHEFILE_ENTRY
* pEntry
;
1816 const URL_CACHEFILE_ENTRY
* pUrlEntry
;
1817 URLCACHECONTAINER
* pContainer
;
1820 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl
), lpCacheEntryInfo
, lpdwCacheEntryInfoBufferSize
);
1822 error
= URLCacheContainers_FindContainerW(lpszUrl
, &pContainer
);
1823 if (error
!= ERROR_SUCCESS
)
1825 SetLastError(error
);
1829 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
1830 if (error
!= ERROR_SUCCESS
)
1832 SetLastError(error
);
1836 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
1839 if (!URLCache_FindHashW(pHeader
, lpszUrl
, &pHashEntry
))
1841 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1842 WARN("entry %s not found!\n", debugstr_w(lpszUrl
));
1843 SetLastError(ERROR_FILE_NOT_FOUND
);
1847 pEntry
= (const CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
1848 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
1850 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1851 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
1852 SetLastError(ERROR_FILE_NOT_FOUND
);
1856 pUrlEntry
= (const URL_CACHEFILE_ENTRY
*)pEntry
;
1857 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
));
1858 TRACE("Header info: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
));
1860 if (lpdwCacheEntryInfoBufferSize
)
1862 if (!lpCacheEntryInfo
)
1863 *lpdwCacheEntryInfoBufferSize
= 0;
1865 error
= URLCache_CopyEntry(
1868 (LPINTERNET_CACHE_ENTRY_INFOA
)lpCacheEntryInfo
,
1869 lpdwCacheEntryInfoBufferSize
,
1871 TRUE
/* UNICODE */);
1872 if (error
!= ERROR_SUCCESS
)
1874 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1875 SetLastError(error
);
1878 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
));
1881 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1886 /***********************************************************************
1887 * GetUrlCacheEntryInfoExW (WININET.@)
1890 BOOL WINAPI
GetUrlCacheEntryInfoExW(
1892 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1893 LPDWORD lpdwCacheEntryInfoBufSize
,
1894 LPWSTR lpszReserved
,
1895 LPDWORD lpdwReserved
,
1899 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1900 debugstr_w(lpszUrl
),
1902 lpdwCacheEntryInfoBufSize
,
1908 if ((lpszReserved
!= NULL
) ||
1909 (lpdwReserved
!= NULL
) ||
1910 (lpReserved
!= NULL
))
1912 ERR("Reserved value was not 0\n");
1913 SetLastError(ERROR_INVALID_PARAMETER
);
1918 FIXME("Undocumented flag(s): %x\n", dwFlags
);
1919 SetLastError(ERROR_FILE_NOT_FOUND
);
1922 return GetUrlCacheEntryInfoW(lpszUrl
, lpCacheEntryInfo
, lpdwCacheEntryInfoBufSize
);
1925 /***********************************************************************
1926 * SetUrlCacheEntryInfoA (WININET.@)
1928 BOOL WINAPI
SetUrlCacheEntryInfoA(
1930 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1931 DWORD dwFieldControl
)
1933 LPURLCACHE_HEADER pHeader
;
1934 struct _HASH_ENTRY
* pHashEntry
;
1935 CACHEFILE_ENTRY
* pEntry
;
1936 URLCACHECONTAINER
* pContainer
;
1939 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
1941 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
1942 if (error
!= ERROR_SUCCESS
)
1944 SetLastError(error
);
1948 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
1949 if (error
!= ERROR_SUCCESS
)
1951 SetLastError(error
);
1955 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
1958 if (!URLCache_FindHash(pHeader
, lpszUrlName
, &pHashEntry
))
1960 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1961 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
1962 SetLastError(ERROR_FILE_NOT_FOUND
);
1966 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
1967 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
1969 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1970 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
1971 SetLastError(ERROR_FILE_NOT_FOUND
);
1975 URLCache_SetEntryInfo(
1976 (URL_CACHEFILE_ENTRY
*)pEntry
,
1977 (const INTERNET_CACHE_ENTRY_INFOW
*)lpCacheEntryInfo
,
1980 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
1985 /***********************************************************************
1986 * SetUrlCacheEntryInfoW (WININET.@)
1988 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
, DWORD dwFieldControl
)
1990 LPURLCACHE_HEADER pHeader
;
1991 struct _HASH_ENTRY
* pHashEntry
;
1992 CACHEFILE_ENTRY
* pEntry
;
1993 URLCACHECONTAINER
* pContainer
;
1996 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl
), lpCacheEntryInfo
, dwFieldControl
);
1998 error
= URLCacheContainers_FindContainerW(lpszUrl
, &pContainer
);
1999 if (error
!= ERROR_SUCCESS
)
2001 SetLastError(error
);
2005 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
2006 if (error
!= ERROR_SUCCESS
)
2008 SetLastError(error
);
2012 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
2015 if (!URLCache_FindHashW(pHeader
, lpszUrl
, &pHashEntry
))
2017 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2018 WARN("entry %s not found!\n", debugstr_w(lpszUrl
));
2019 SetLastError(ERROR_FILE_NOT_FOUND
);
2023 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
2024 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
2026 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2027 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
2028 SetLastError(ERROR_FILE_NOT_FOUND
);
2032 URLCache_SetEntryInfo(
2033 (URL_CACHEFILE_ENTRY
*)pEntry
,
2037 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2042 /***********************************************************************
2043 * RetrieveUrlCacheEntryFileA (WININET.@)
2046 BOOL WINAPI
RetrieveUrlCacheEntryFileA(
2047 IN LPCSTR lpszUrlName
,
2048 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2049 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
2053 LPURLCACHE_HEADER pHeader
;
2054 struct _HASH_ENTRY
* pHashEntry
;
2055 CACHEFILE_ENTRY
* pEntry
;
2056 URL_CACHEFILE_ENTRY
* pUrlEntry
;
2057 URLCACHECONTAINER
* pContainer
;
2060 TRACE("(%s, %p, %p, 0x%08x)\n",
2061 debugstr_a(lpszUrlName
),
2063 lpdwCacheEntryInfoBufferSize
,
2066 if (!lpszUrlName
|| !lpdwCacheEntryInfoBufferSize
||
2067 (!lpCacheEntryInfo
&& *lpdwCacheEntryInfoBufferSize
))
2069 SetLastError(ERROR_INVALID_PARAMETER
);
2073 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
2074 if (error
!= ERROR_SUCCESS
)
2076 SetLastError(error
);
2080 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
2081 if (error
!= ERROR_SUCCESS
)
2083 SetLastError(error
);
2087 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
2090 if (!URLCache_FindHash(pHeader
, lpszUrlName
, &pHashEntry
))
2092 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2093 TRACE("entry %s not found!\n", lpszUrlName
);
2094 SetLastError(ERROR_FILE_NOT_FOUND
);
2098 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
2099 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
2101 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2102 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
2103 SetLastError(ERROR_FILE_NOT_FOUND
);
2107 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
2108 if (!pUrlEntry
->dwOffsetLocalName
)
2110 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2111 SetLastError(ERROR_INVALID_DATA
);
2115 TRACE("Found URL: %s\n", (LPSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
);
2116 TRACE("Header info: %s\n", (LPBYTE
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
);
2118 error
= URLCache_CopyEntry(pContainer
, pHeader
, lpCacheEntryInfo
,
2119 lpdwCacheEntryInfoBufferSize
, pUrlEntry
,
2121 if (error
!= ERROR_SUCCESS
)
2123 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2124 SetLastError(error
);
2127 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
));
2129 pUrlEntry
->dwHitRate
++;
2130 pUrlEntry
->dwUseCount
++;
2131 URLCache_HashEntrySetFlags(pHashEntry
, HASHTABLE_LOCK
);
2132 GetSystemTimeAsFileTime(&pUrlEntry
->LastAccessTime
);
2134 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2139 /***********************************************************************
2140 * RetrieveUrlCacheEntryFileW (WININET.@)
2143 BOOL WINAPI
RetrieveUrlCacheEntryFileW(
2144 IN LPCWSTR lpszUrlName
,
2145 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2146 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
2150 LPURLCACHE_HEADER pHeader
;
2151 struct _HASH_ENTRY
* pHashEntry
;
2152 CACHEFILE_ENTRY
* pEntry
;
2153 URL_CACHEFILE_ENTRY
* pUrlEntry
;
2154 URLCACHECONTAINER
* pContainer
;
2157 TRACE("(%s, %p, %p, 0x%08x)\n",
2158 debugstr_w(lpszUrlName
),
2160 lpdwCacheEntryInfoBufferSize
,
2163 if (!lpszUrlName
|| !lpdwCacheEntryInfoBufferSize
||
2164 (!lpCacheEntryInfo
&& *lpdwCacheEntryInfoBufferSize
))
2166 SetLastError(ERROR_INVALID_PARAMETER
);
2170 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
2171 if (error
!= ERROR_SUCCESS
)
2173 SetLastError(error
);
2177 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
2178 if (error
!= ERROR_SUCCESS
)
2180 SetLastError(error
);
2184 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
2187 if (!URLCache_FindHashW(pHeader
, lpszUrlName
, &pHashEntry
))
2189 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2190 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName
));
2191 SetLastError(ERROR_FILE_NOT_FOUND
);
2195 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
2196 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
2198 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2199 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
2200 SetLastError(ERROR_FILE_NOT_FOUND
);
2204 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
2205 if (!pUrlEntry
->dwOffsetLocalName
)
2207 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2208 SetLastError(ERROR_INVALID_DATA
);
2212 TRACE("Found URL: %s\n", (LPSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
);
2213 TRACE("Header info: %s\n", (LPBYTE
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
);
2215 error
= URLCache_CopyEntry(
2218 (LPINTERNET_CACHE_ENTRY_INFOA
)lpCacheEntryInfo
,
2219 lpdwCacheEntryInfoBufferSize
,
2221 TRUE
/* UNICODE */);
2222 if (error
!= ERROR_SUCCESS
)
2224 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2225 SetLastError(error
);
2228 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
));
2230 pUrlEntry
->dwHitRate
++;
2231 pUrlEntry
->dwUseCount
++;
2232 URLCache_HashEntrySetFlags(pHashEntry
, HASHTABLE_LOCK
);
2233 GetSystemTimeAsFileTime(&pUrlEntry
->LastAccessTime
);
2235 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2240 static BOOL
DeleteUrlCacheEntryInternal(const URLCACHECONTAINER
* pContainer
,
2241 LPURLCACHE_HEADER pHeader
, struct _HASH_ENTRY
*pHashEntry
)
2243 CACHEFILE_ENTRY
* pEntry
;
2244 URL_CACHEFILE_ENTRY
* pUrlEntry
;
2246 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
2247 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
2249 FIXME("Trying to delete entry of unknown format %s\n",
2250 debugstr_an((LPCSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
2251 SetLastError(ERROR_FILE_NOT_FOUND
);
2255 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
2256 if(URLCache_IsLocked(pHashEntry
, pUrlEntry
))
2258 TRACE("Trying to delete locked entry\n");
2259 pUrlEntry
->CacheEntryType
|= PENDING_DELETE_CACHE_ENTRY
;
2260 SetLastError(ERROR_SHARING_VIOLATION
);
2264 if(!URLCache_DeleteFile(pContainer
, pHeader
, pUrlEntry
))
2266 URLCache_DeleteEntry(pHeader
, pEntry
);
2270 /* Add entry to leaked files list */
2271 pUrlEntry
->CacheFileEntry
.dwSignature
= LEAK_SIGNATURE
;
2272 pUrlEntry
->dwExemptDelta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2273 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->dwOffsetEntry
;
2276 URLCache_DeleteEntryFromHash(pHashEntry
);
2280 static HANDLE free_cache_running
;
2281 static HANDLE dll_unload_event
;
2282 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2284 FreeUrlCacheSpaceW(NULL
, 20, 0);
2285 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2289 static void handle_full_cache(void)
2291 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2292 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2293 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2297 /* Enumerates entires in cache, allows cache unlocking between calls. */
2298 static BOOL
urlcache_next_entry(URLCACHE_HEADER
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2299 struct _HASH_ENTRY
**hash_entry
, CACHEFILE_ENTRY
**entry
)
2301 HASH_CACHEFILE_ENTRY
*hashtable_entry
;
2306 if(!*hash_table_off
) {
2307 *hash_table_off
= header
->dwOffsetFirstHashTable
;
2308 *hash_table_entry
= 0;
2310 hashtable_entry
= URLCache_HashEntryFromOffset(header
, *hash_table_off
);
2312 if(*hash_table_off
>= header
->dwFileSize
) {
2313 *hash_table_off
= 0;
2317 hashtable_entry
= URLCache_HashEntryFromOffset(header
, *hash_table_off
);
2320 if(hashtable_entry
->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
) {
2321 *hash_table_off
= 0;
2326 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2327 *hash_table_off
= hashtable_entry
->dwAddressNext
;
2328 if(!*hash_table_off
) {
2329 *hash_table_off
= 0;
2333 hashtable_entry
= URLCache_HashEntryFromOffset(header
, *hash_table_off
);
2334 *hash_table_entry
= 0;
2337 if(hashtable_entry
->HashTable
[*hash_table_entry
].dwHashKey
!= HASHTABLE_DEL
&&
2338 hashtable_entry
->HashTable
[*hash_table_entry
].dwHashKey
!= HASHTABLE_FREE
) {
2339 *hash_entry
= &hashtable_entry
->HashTable
[*hash_table_entry
];
2340 *entry
= (CACHEFILE_ENTRY
*)((LPBYTE
)header
+ hashtable_entry
->HashTable
[*hash_table_entry
].dwOffsetEntry
);
2341 (*hash_table_entry
)++;
2345 (*hash_table_entry
)++;
2348 *hash_table_off
= 0;
2352 /* Rates an urlcache entry to determine if it can be deleted.
2354 * Score 0 means that entry can safely be removed, the bigger rating
2355 * the smaller chance of entry being removed.
2356 * DWORD_MAX means that entry can't be deleted at all.
2358 * Rating system is currently not fully compatible with native implementation.
2360 static DWORD
urlcache_rate_entry(URL_CACHEFILE_ENTRY
*url_entry
, FILETIME
*cur_time
)
2362 ULARGE_INTEGER time
, access_time
;
2365 access_time
.u
.LowPart
= url_entry
->LastAccessTime
.dwLowDateTime
;
2366 access_time
.u
.HighPart
= url_entry
->LastAccessTime
.dwHighDateTime
;
2368 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2369 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2371 /* Don't touch entries that were added less then 10 minutes ago */
2372 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2375 if(url_entry
->CacheEntryType
& STICKY_CACHE_ENTRY
)
2376 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->dwExemptDelta
*FILETIME_SECOND
)
2379 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2380 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2382 if(url_entry
->dwHitRate
> 100)
2385 rating
+= url_entry
->dwHitRate
;
2390 static int dword_cmp(const void *p1
, const void *p2
)
2392 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2395 /***********************************************************************
2396 * FreeUrlCacheSpaceW (WININET.@)
2398 * Frees up some cache.
2401 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2402 * size [I] How much percent of cache space should be free.
2403 * filter [I] Which entries can't be deleted (CacheEntryType)
2406 * TRUE success. FALSE failure.
2409 * This implementation just retrieves the path of the cache directory, and
2410 * deletes its contents from the filesystem. The correct approach would
2411 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2413 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2415 URLCACHECONTAINER
*container
;
2418 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2420 if(size
<1 || size
>100) {
2421 SetLastError(ERROR_INVALID_PARAMETER
);
2426 FIXME("cache_path != NULL not supported yet\n");
2427 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2431 if(size
==100 && !filter
) {
2432 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, URLCACHECONTAINER
, entry
)
2434 /* The URL cache has prefix L"" (unlike Cookies and History) */
2435 if (container
->cache_prefix
[0] == 0)
2439 WaitForSingleObject(container
->hMutex
, INFINITE
);
2441 /* unlock, delete, recreate and lock cache */
2442 URLCacheContainer_CloseIndex(container
);
2443 ret_del
= URLCache_DeleteCacheDirectory(container
->path
);
2444 err
= URLCacheContainer_OpenIndex(container
, MIN_BLOCK_NO
);
2446 ReleaseMutex(container
->hMutex
);
2447 return ret_del
&& (err
== ERROR_SUCCESS
);
2452 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, URLCACHECONTAINER
, entry
)
2454 URLCACHE_HEADER
*header
;
2455 struct _HASH_ENTRY
*hash_entry
;
2456 CACHEFILE_ENTRY
*entry
;
2457 URL_CACHEFILE_ENTRY
*url_entry
;
2458 ULONGLONG desired_size
, cur_size
;
2459 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2460 DWORD rate
[100], rate_no
;
2463 err
= URLCacheContainer_OpenIndex(container
, MIN_BLOCK_NO
);
2464 if(err
!= ERROR_SUCCESS
)
2467 header
= URLCacheContainer_LockIndex(container
);
2471 urlcache_clean_leaked_entries(container
, header
);
2473 desired_size
= header
->CacheLimit
.QuadPart
*(100-size
)/100;
2474 cur_size
= header
->CacheUsage
.QuadPart
+header
->ExemptUsage
.QuadPart
;
2475 if(cur_size
<= desired_size
)
2478 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2480 if(!delete_factor
) {
2481 URLCacheContainer_UnlockIndex(container
, header
);
2486 hash_table_entry
= 0;
2488 GetSystemTimeAsFileTime(&cur_time
);
2489 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2490 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2491 if(entry
->dwSignature
!= URL_SIGNATURE
) {
2492 WARN("only url entries are currently supported\n");
2496 url_entry
= (URL_CACHEFILE_ENTRY
*)entry
;
2497 if(url_entry
->CacheEntryType
& filter
)
2500 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2501 if(rate
[rate_no
] != -1)
2506 TRACE("nothing to delete\n");
2507 URLCacheContainer_UnlockIndex(container
, header
);
2511 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2513 delete_factor
= delete_factor
*rate_no
/100;
2514 delete_factor
= rate
[delete_factor
];
2515 TRACE("deleting files with rating %d or less\n", delete_factor
);
2518 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2519 if(entry
->dwSignature
!= URL_SIGNATURE
)
2522 url_entry
= (URL_CACHEFILE_ENTRY
*)entry
;
2523 if(url_entry
->CacheEntryType
& filter
)
2526 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2527 TRACE("deleting file: %s\n", (char*)url_entry
+url_entry
->dwOffsetLocalName
);
2528 DeleteUrlCacheEntryInternal(container
, header
, hash_entry
);
2530 if(header
->CacheUsage
.QuadPart
+header
->ExemptUsage
.QuadPart
<= desired_size
)
2533 /* Allow other threads to use cache while cleaning */
2534 URLCacheContainer_UnlockIndex(container
, header
);
2535 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2536 TRACE("got dll_unload_event - finishing\n");
2540 header
= URLCacheContainer_LockIndex(container
);
2544 TRACE("cache size after cleaning 0x%s/0x%s\n",
2545 wine_dbgstr_longlong(header
->CacheUsage
.QuadPart
+header
->ExemptUsage
.QuadPart
),
2546 wine_dbgstr_longlong(header
->CacheLimit
.QuadPart
));
2547 URLCacheContainer_UnlockIndex(container
, header
);
2553 /***********************************************************************
2554 * FreeUrlCacheSpaceA (WININET.@)
2556 * See FreeUrlCacheSpaceW.
2558 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwSizeType
)
2561 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2562 if (lpszCachePath
== NULL
|| path
!= NULL
)
2563 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwSizeType
);
2568 /***********************************************************************
2569 * UnlockUrlCacheEntryFileA (WININET.@)
2572 BOOL WINAPI
UnlockUrlCacheEntryFileA(
2573 IN LPCSTR lpszUrlName
,
2577 LPURLCACHE_HEADER pHeader
;
2578 struct _HASH_ENTRY
* pHashEntry
;
2579 CACHEFILE_ENTRY
* pEntry
;
2580 URL_CACHEFILE_ENTRY
* pUrlEntry
;
2581 URLCACHECONTAINER
* pContainer
;
2584 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2588 ERR("dwReserved != 0\n");
2589 SetLastError(ERROR_INVALID_PARAMETER
);
2593 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
2594 if (error
!= ERROR_SUCCESS
)
2596 SetLastError(error
);
2600 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
2601 if (error
!= ERROR_SUCCESS
)
2603 SetLastError(error
);
2607 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
2610 if (!URLCache_FindHash(pHeader
, lpszUrlName
, &pHashEntry
))
2612 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2613 TRACE("entry %s not found!\n", lpszUrlName
);
2614 SetLastError(ERROR_FILE_NOT_FOUND
);
2618 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
2619 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
2621 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2622 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
2623 SetLastError(ERROR_FILE_NOT_FOUND
);
2627 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
2629 if (pUrlEntry
->dwUseCount
== 0)
2631 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2634 pUrlEntry
->dwUseCount
--;
2635 if (!pUrlEntry
->dwUseCount
)
2637 URLCache_HashEntrySetFlags(pHashEntry
, HASHTABLE_URL
);
2638 if (pUrlEntry
->CacheEntryType
& PENDING_DELETE_CACHE_ENTRY
)
2639 DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
2642 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2647 /***********************************************************************
2648 * UnlockUrlCacheEntryFileW (WININET.@)
2651 BOOL WINAPI
UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName
, DWORD dwReserved
)
2653 LPURLCACHE_HEADER pHeader
;
2654 struct _HASH_ENTRY
* pHashEntry
;
2655 CACHEFILE_ENTRY
* pEntry
;
2656 URL_CACHEFILE_ENTRY
* pUrlEntry
;
2657 URLCACHECONTAINER
* pContainer
;
2660 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName
), dwReserved
);
2664 ERR("dwReserved != 0\n");
2665 SetLastError(ERROR_INVALID_PARAMETER
);
2669 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
2670 if (error
!= ERROR_SUCCESS
)
2672 SetLastError(error
);
2676 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
2677 if (error
!= ERROR_SUCCESS
)
2679 SetLastError(error
);
2683 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
2686 if (!URLCache_FindHashW(pHeader
, lpszUrlName
, &pHashEntry
))
2688 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2689 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName
));
2690 SetLastError(ERROR_FILE_NOT_FOUND
);
2694 pEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
2695 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
2697 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2698 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
2699 SetLastError(ERROR_FILE_NOT_FOUND
);
2703 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
2705 if (pUrlEntry
->dwUseCount
== 0)
2707 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2710 pUrlEntry
->dwUseCount
--;
2711 if (!pUrlEntry
->dwUseCount
)
2712 URLCache_HashEntrySetFlags(pHashEntry
, HASHTABLE_URL
);
2714 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2719 /***********************************************************************
2720 * CreateUrlCacheEntryA (WININET.@)
2723 BOOL WINAPI
CreateUrlCacheEntryA(
2724 IN LPCSTR lpszUrlName
,
2725 IN DWORD dwExpectedFileSize
,
2726 IN LPCSTR lpszFileExtension
,
2727 OUT LPSTR lpszFileName
,
2732 WCHAR
*file_extension
= NULL
;
2733 WCHAR file_name
[MAX_PATH
];
2734 BOOL bSuccess
= FALSE
;
2737 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName
), dwExpectedFileSize
,
2738 debugstr_a(lpszFileExtension
), lpszFileName
, dwReserved
);
2740 if (lpszUrlName
&& (url_name
= heap_strdupAtoW(lpszUrlName
)))
2742 if (!lpszFileExtension
|| (file_extension
= heap_strdupAtoW(lpszFileExtension
)))
2744 if (CreateUrlCacheEntryW(url_name
, dwExpectedFileSize
, file_extension
, file_name
, dwReserved
))
2746 if (WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
) < MAX_PATH
)
2752 dwError
= GetLastError();
2757 dwError
= GetLastError();
2759 heap_free(file_extension
);
2763 dwError
= GetLastError();
2765 heap_free(url_name
);
2766 if (!bSuccess
) SetLastError(dwError
);
2770 /***********************************************************************
2771 * CreateUrlCacheEntryW (WININET.@)
2774 BOOL WINAPI
CreateUrlCacheEntryW(
2775 IN LPCWSTR lpszUrlName
,
2776 IN DWORD dwExpectedFileSize
,
2777 IN LPCWSTR lpszFileExtension
,
2778 OUT LPWSTR lpszFileName
,
2782 URLCACHECONTAINER
* pContainer
;
2783 LPURLCACHE_HEADER pHeader
;
2784 CHAR szFile
[MAX_PATH
];
2785 WCHAR szExtension
[MAX_PATH
];
2786 LPCWSTR lpszUrlPart
;
2788 LPCWSTR lpszFileNameExtension
;
2789 LPWSTR lpszFileNameNoPath
;
2791 int countnoextension
;
2794 BOOL bFound
= FALSE
;
2800 static const WCHAR szWWW
[] = {'w','w','w',0};
2801 static const WCHAR fmt
[] = {'%','0','8','X','%','s',0};
2803 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2804 debugstr_w(lpszUrlName
),
2806 debugstr_w(lpszFileExtension
),
2811 FIXME("dwReserved 0x%08x\n", dwReserved
);
2813 lpszUrlEnd
= lpszUrlName
+ strlenW(lpszUrlName
);
2815 if (((lpszUrlEnd
- lpszUrlName
) > 1) && (*(lpszUrlEnd
- 1) == '/' || *(lpszUrlEnd
- 1) == '\\'))
2818 lpszUrlPart
= memchrW(lpszUrlName
, '?', lpszUrlEnd
- lpszUrlName
);
2820 lpszUrlPart
= memchrW(lpszUrlName
, '#', lpszUrlEnd
- lpszUrlName
);
2822 lpszUrlEnd
= lpszUrlPart
;
2824 for (lpszUrlPart
= lpszUrlEnd
;
2825 (lpszUrlPart
>= lpszUrlName
);
2828 if ((*lpszUrlPart
== '/' || *lpszUrlPart
== '\\') && ((lpszUrlEnd
- lpszUrlPart
) > 1))
2835 if (!lstrcmpW(lpszUrlPart
, szWWW
))
2837 lpszUrlPart
+= lstrlenW(szWWW
);
2840 count
= lpszUrlEnd
- lpszUrlPart
;
2842 if (bFound
&& (count
< MAX_PATH
))
2844 int len
= WideCharToMultiByte(CP_ACP
, 0, lpszUrlPart
, count
, szFile
, sizeof(szFile
) - 1, NULL
, NULL
);
2848 while(len
&& szFile
[--len
] == '/') szFile
[len
] = '\0';
2850 /* FIXME: get rid of illegal characters like \, / and : */
2854 FIXME("need to generate a random filename\n");
2857 TRACE("File name: %s\n", debugstr_a(szFile
));
2859 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
2860 if (error
!= ERROR_SUCCESS
)
2862 SetLastError(error
);
2866 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
2867 if (error
!= ERROR_SUCCESS
)
2869 SetLastError(error
);
2873 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
2876 CacheDir
= (BYTE
)(rand() % pHeader
->DirectoryCount
);
2878 lBufferSize
= MAX_PATH
* sizeof(WCHAR
);
2879 if (!URLCache_LocalFileNameToPathW(pContainer
, pHeader
, szFile
, CacheDir
, lpszFileName
, &lBufferSize
))
2881 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2882 debugstr_a(szFile
), lBufferSize
);
2883 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2887 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
2889 for (lpszFileNameNoPath
= lpszFileName
+ lBufferSize
/ sizeof(WCHAR
) - 2;
2890 lpszFileNameNoPath
>= lpszFileName
;
2891 --lpszFileNameNoPath
)
2893 if (*lpszFileNameNoPath
== '/' || *lpszFileNameNoPath
== '\\')
2897 countnoextension
= lstrlenW(lpszFileNameNoPath
);
2898 lpszFileNameExtension
= PathFindExtensionW(lpszFileNameNoPath
);
2899 if (lpszFileNameExtension
)
2900 countnoextension
-= lstrlenW(lpszFileNameExtension
);
2901 *szExtension
= '\0';
2903 if (lpszFileExtension
)
2905 szExtension
[0] = '.';
2906 lstrcpyW(szExtension
+1, lpszFileExtension
);
2909 for (i
= 0; i
< 255; i
++)
2911 static const WCHAR szFormat
[] = {'[','%','u',']','%','s',0};
2914 wsprintfW(lpszFileNameNoPath
+ countnoextension
, szFormat
, i
, szExtension
);
2915 for (p
= lpszFileNameNoPath
+ 1; *p
; p
++)
2921 case '/': case '\\':
2928 if (p
[-1] == ' ' || p
[-1] == '.') p
[-1] = '_';
2930 TRACE("Trying: %s\n", debugstr_w(lpszFileName
));
2931 hFile
= CreateFileW(lpszFileName
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2932 if (hFile
!= INVALID_HANDLE_VALUE
)
2939 GetSystemTimeAsFileTime(&ft
);
2940 wsprintfW(lpszFileNameNoPath
+ countnoextension
, fmt
, ft
.dwLowDateTime
, szExtension
);
2942 TRACE("Trying: %s\n", debugstr_w(lpszFileName
));
2943 hFile
= CreateFileW(lpszFileName
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2944 if (hFile
!= INVALID_HANDLE_VALUE
)
2950 WARN("Could not find a unique filename\n");
2954 /***********************************************************************
2955 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2957 * The bug we are compensating for is that some drongo at Microsoft
2958 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2959 * As a consequence, CommitUrlCacheEntryA has been effectively
2960 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2961 * is still defined as LPCWSTR. The result (other than madness) is
2962 * that we always need to store lpHeaderInfo in CP_ACP rather than
2963 * in UTF16, and we need to avoid converting lpHeaderInfo in
2964 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2965 * result will lose data for arbitrary binary data.
2968 static BOOL
CommitUrlCacheEntryInternal(
2969 IN LPCWSTR lpszUrlName
,
2970 IN LPCWSTR lpszLocalFileName
,
2971 IN FILETIME ExpireTime
,
2972 IN FILETIME LastModifiedTime
,
2973 IN DWORD CacheEntryType
,
2974 IN LPBYTE lpHeaderInfo
,
2975 IN DWORD dwHeaderSize
,
2976 IN LPCWSTR lpszFileExtension
,
2977 IN LPCWSTR lpszOriginalUrl
2980 URLCACHECONTAINER
* pContainer
;
2981 LPURLCACHE_HEADER pHeader
;
2982 struct _HASH_ENTRY
* pHashEntry
;
2983 CACHEFILE_ENTRY
* pEntry
;
2984 URL_CACHEFILE_ENTRY
* pUrlEntry
;
2985 DWORD url_entry_offset
;
2986 DWORD dwBytesNeeded
= DWORD_ALIGN(sizeof(*pUrlEntry
));
2987 DWORD dwOffsetLocalFileName
= 0;
2988 DWORD dwOffsetHeader
= 0;
2989 DWORD dwOffsetFileExtension
= 0;
2990 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2991 LARGE_INTEGER file_size
;
2992 BYTE cDirectory
= 0;
2993 char achFile
[MAX_PATH
];
2994 LPSTR lpszUrlNameA
= NULL
;
2995 LPSTR lpszFileExtensionA
= NULL
;
2996 char *pchLocalFileName
= 0;
2998 DWORD exempt_delta
= 0;
3001 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3002 debugstr_w(lpszUrlName
),
3003 debugstr_w(lpszLocalFileName
),
3007 debugstr_w(lpszFileExtension
),
3008 debugstr_w(lpszOriginalUrl
));
3010 if (CacheEntryType
& STICKY_CACHE_ENTRY
&& !lpszLocalFileName
)
3012 SetLastError(ERROR_INVALID_PARAMETER
);
3015 if (lpszOriginalUrl
)
3016 WARN(": lpszOriginalUrl ignored\n");
3018 memset(&file_attr
, 0, sizeof(file_attr
));
3019 if (lpszLocalFileName
)
3021 if(!GetFileAttributesExW(lpszLocalFileName
, GetFileExInfoStandard
, &file_attr
))
3024 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
3025 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
3027 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
3028 if (error
!= ERROR_SUCCESS
)
3030 SetLastError(error
);
3034 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
3035 if (error
!= ERROR_SUCCESS
)
3037 SetLastError(error
);
3041 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
3044 lpszUrlNameA
= heap_strdupWtoA(lpszUrlName
);
3047 error
= GetLastError();
3051 if (lpszFileExtension
&& !(lpszFileExtensionA
= heap_strdupWtoA(lpszFileExtension
)))
3053 error
= GetLastError();
3057 if (URLCache_FindHash(pHeader
, lpszUrlNameA
, &pHashEntry
))
3059 URL_CACHEFILE_ENTRY
*pUrlEntry
= (URL_CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
3060 if (URLCache_IsLocked(pHashEntry
, pUrlEntry
))
3062 TRACE("Trying to overwrite locked entry\n");
3063 error
= ERROR_SHARING_VIOLATION
;
3067 hit_rate
= pUrlEntry
->dwHitRate
;
3068 exempt_delta
= pUrlEntry
->dwExemptDelta
;
3069 DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
3072 if (lpszLocalFileName
)
3074 BOOL bFound
= FALSE
;
3076 if (strncmpW(lpszLocalFileName
, pContainer
->path
, lstrlenW(pContainer
->path
)))
3078 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName
), debugstr_w(pContainer
->path
));
3079 error
= ERROR_INVALID_PARAMETER
;
3083 /* skip container path prefix */
3084 lpszLocalFileName
+= lstrlenW(pContainer
->path
);
3086 WideCharToMultiByte(CP_ACP
, 0, lpszLocalFileName
, -1, achFile
, MAX_PATH
, NULL
, NULL
);
3087 pchLocalFileName
= achFile
;
3089 for (cDirectory
= 0; cDirectory
< pHeader
->DirectoryCount
; cDirectory
++)
3091 if (!strncmp(pHeader
->directory_data
[cDirectory
].filename
, pchLocalFileName
, DIR_LENGTH
))
3100 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName
));
3101 error
= ERROR_INVALID_PARAMETER
;
3105 lpszLocalFileName
+= DIR_LENGTH
+ 1;
3106 pchLocalFileName
+= DIR_LENGTH
+ 1;
3109 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ strlen(lpszUrlNameA
) + 1);
3110 if (lpszLocalFileName
)
3112 dwOffsetLocalFileName
= dwBytesNeeded
;
3113 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ strlen(pchLocalFileName
) + 1);
3117 dwOffsetHeader
= dwBytesNeeded
;
3118 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ dwHeaderSize
);
3120 if (lpszFileExtensionA
)
3122 dwOffsetFileExtension
= dwBytesNeeded
;
3123 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ strlen(lpszFileExtensionA
) + 1);
3126 /* round up to next block */
3127 if (dwBytesNeeded
% BLOCKSIZE
)
3129 dwBytesNeeded
-= dwBytesNeeded
% BLOCKSIZE
;
3130 dwBytesNeeded
+= BLOCKSIZE
;
3133 error
= URLCache_FindFirstFreeEntry(pHeader
, dwBytesNeeded
/ BLOCKSIZE
, &pEntry
);
3134 while (error
== ERROR_HANDLE_DISK_FULL
)
3136 error
= URLCacheContainer_CleanIndex(pContainer
, &pHeader
);
3137 if (error
== ERROR_SUCCESS
)
3138 error
= URLCache_FindFirstFreeEntry(pHeader
, dwBytesNeeded
/ BLOCKSIZE
, &pEntry
);
3140 if (error
!= ERROR_SUCCESS
)
3143 /* FindFirstFreeEntry fills in blocks used */
3144 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
3145 url_entry_offset
= (LPBYTE
)pUrlEntry
- (LPBYTE
)pHeader
;
3146 pUrlEntry
->CacheFileEntry
.dwSignature
= URL_SIGNATURE
;
3147 pUrlEntry
->CacheDir
= cDirectory
;
3148 pUrlEntry
->CacheEntryType
= CacheEntryType
| pContainer
->default_entry_type
;
3149 pUrlEntry
->dwHeaderInfoSize
= dwHeaderSize
;
3150 if ((CacheEntryType
& STICKY_CACHE_ENTRY
) && !exempt_delta
)
3152 /* Sticky entries have a default exempt time of one day */
3153 exempt_delta
= 86400;
3155 pUrlEntry
->dwExemptDelta
= exempt_delta
;
3156 pUrlEntry
->dwHitRate
= hit_rate
+1;
3157 pUrlEntry
->dwOffsetFileExtension
= dwOffsetFileExtension
;
3158 pUrlEntry
->dwOffsetHeaderInfo
= dwOffsetHeader
;
3159 pUrlEntry
->dwOffsetLocalName
= dwOffsetLocalFileName
;
3160 pUrlEntry
->dwOffsetUrl
= DWORD_ALIGN(sizeof(*pUrlEntry
));
3161 pUrlEntry
->size
.QuadPart
= file_size
.QuadPart
;
3162 pUrlEntry
->dwUseCount
= 0;
3163 GetSystemTimeAsFileTime(&pUrlEntry
->LastAccessTime
);
3164 pUrlEntry
->LastModifiedTime
= LastModifiedTime
;
3165 URLCache_FileTimeToDosDateTime(&pUrlEntry
->LastAccessTime
, &pUrlEntry
->wLastSyncDate
, &pUrlEntry
->wLastSyncTime
);
3166 URLCache_FileTimeToDosDateTime(&ExpireTime
, &pUrlEntry
->wExpiredDate
, &pUrlEntry
->wExpiredTime
);
3167 URLCache_FileTimeToDosDateTime(&file_attr
.ftLastWriteTime
, &pUrlEntry
->LastWriteDate
, &pUrlEntry
->LastWriteTime
);
3170 pUrlEntry
->dwUnknown1
= 0;
3171 pUrlEntry
->dwUnknown2
= 0;
3172 pUrlEntry
->dwUnknown3
= 0x60;
3173 pUrlEntry
->Unknown4
= 0;
3174 pUrlEntry
->wUnknown5
= 0x1010;
3175 pUrlEntry
->dwUnknown7
= 0;
3176 pUrlEntry
->dwUnknown8
= 0;
3179 strcpy((LPSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
, lpszUrlNameA
);
3180 if (dwOffsetLocalFileName
)
3181 strcpy((LPSTR
)((LPBYTE
)pUrlEntry
+ dwOffsetLocalFileName
), pchLocalFileName
);
3183 memcpy((LPBYTE
)pUrlEntry
+ dwOffsetHeader
, lpHeaderInfo
, dwHeaderSize
);
3184 if (dwOffsetFileExtension
)
3185 strcpy((LPSTR
)((LPBYTE
)pUrlEntry
+ dwOffsetFileExtension
), lpszFileExtensionA
);
3187 error
= URLCache_AddEntryToHash(pHeader
, lpszUrlNameA
, url_entry_offset
, HASHTABLE_URL
);
3188 while (error
== ERROR_HANDLE_DISK_FULL
)
3190 error
= URLCacheContainer_CleanIndex(pContainer
, &pHeader
);
3191 if (error
== ERROR_SUCCESS
)
3193 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ url_entry_offset
);
3194 error
= URLCache_AddEntryToHash(pHeader
, lpszUrlNameA
,
3195 url_entry_offset
, HASHTABLE_URL
);
3198 if (error
!= ERROR_SUCCESS
)
3199 URLCache_DeleteEntry(pHeader
, &pUrlEntry
->CacheFileEntry
);
3202 if (pUrlEntry
->CacheDir
< pHeader
->DirectoryCount
)
3203 pHeader
->directory_data
[pUrlEntry
->CacheDir
].dwNumFiles
++;
3204 if (CacheEntryType
& STICKY_CACHE_ENTRY
)
3205 pHeader
->ExemptUsage
.QuadPart
+= file_size
.QuadPart
;
3207 pHeader
->CacheUsage
.QuadPart
+= file_size
.QuadPart
;
3208 if (pHeader
->CacheUsage
.QuadPart
+ pHeader
->ExemptUsage
.QuadPart
>
3209 pHeader
->CacheLimit
.QuadPart
)
3210 handle_full_cache();
3214 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3215 heap_free(lpszUrlNameA
);
3216 heap_free(lpszFileExtensionA
);
3218 if (error
== ERROR_SUCCESS
)
3222 SetLastError(error
);
3227 /***********************************************************************
3228 * CommitUrlCacheEntryA (WININET.@)
3231 BOOL WINAPI
CommitUrlCacheEntryA(
3232 IN LPCSTR lpszUrlName
,
3233 IN LPCSTR lpszLocalFileName
,
3234 IN FILETIME ExpireTime
,
3235 IN FILETIME LastModifiedTime
,
3236 IN DWORD CacheEntryType
,
3237 IN LPBYTE lpHeaderInfo
,
3238 IN DWORD dwHeaderSize
,
3239 IN LPCSTR lpszFileExtension
,
3240 IN LPCSTR lpszOriginalUrl
3243 WCHAR
*url_name
= NULL
;
3244 WCHAR
*local_file_name
= NULL
;
3245 WCHAR
*original_url
= NULL
;
3246 WCHAR
*file_extension
= NULL
;
3247 BOOL bSuccess
= FALSE
;
3249 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3250 debugstr_a(lpszUrlName
),
3251 debugstr_a(lpszLocalFileName
),
3255 debugstr_a(lpszFileExtension
),
3256 debugstr_a(lpszOriginalUrl
));
3258 url_name
= heap_strdupAtoW(lpszUrlName
);
3262 if (lpszLocalFileName
)
3264 local_file_name
= heap_strdupAtoW(lpszLocalFileName
);
3265 if (!local_file_name
)
3268 if (lpszFileExtension
)
3270 file_extension
= heap_strdupAtoW(lpszFileExtension
);
3271 if (!file_extension
)
3274 if (lpszOriginalUrl
)
3276 original_url
= heap_strdupAtoW(lpszOriginalUrl
);
3281 bSuccess
= CommitUrlCacheEntryInternal(url_name
, local_file_name
, ExpireTime
, LastModifiedTime
,
3282 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
,
3283 file_extension
, original_url
);
3286 heap_free(original_url
);
3287 heap_free(file_extension
);
3288 heap_free(local_file_name
);
3289 heap_free(url_name
);
3293 /***********************************************************************
3294 * CommitUrlCacheEntryW (WININET.@)
3297 BOOL WINAPI
CommitUrlCacheEntryW(
3298 IN LPCWSTR lpszUrlName
,
3299 IN LPCWSTR lpszLocalFileName
,
3300 IN FILETIME ExpireTime
,
3301 IN FILETIME LastModifiedTime
,
3302 IN DWORD CacheEntryType
,
3303 IN LPWSTR lpHeaderInfo
,
3304 IN DWORD dwHeaderSize
,
3305 IN LPCWSTR lpszFileExtension
,
3306 IN LPCWSTR lpszOriginalUrl
3310 BOOL bSuccess
= FALSE
;
3312 CHAR
*header_info
= NULL
;
3314 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3315 debugstr_w(lpszUrlName
),
3316 debugstr_w(lpszLocalFileName
),
3320 debugstr_w(lpszFileExtension
),
3321 debugstr_w(lpszOriginalUrl
));
3323 if (!lpHeaderInfo
|| (header_info
= heap_strdupWtoA(lpHeaderInfo
)))
3325 if (CommitUrlCacheEntryInternal(lpszUrlName
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3326 CacheEntryType
, (LPBYTE
)header_info
, len
, lpszFileExtension
, lpszOriginalUrl
))
3332 dwError
= GetLastError();
3336 heap_free(header_info
);
3338 SetLastError(dwError
);
3344 /***********************************************************************
3345 * ReadUrlCacheEntryStream (WININET.@)
3348 BOOL WINAPI
ReadUrlCacheEntryStream(
3349 IN HANDLE hUrlCacheStream
,
3350 IN DWORD dwLocation
,
3351 IN OUT LPVOID lpBuffer
,
3352 IN OUT LPDWORD lpdwLen
,
3356 /* Get handle to file from 'stream' */
3357 STREAM_HANDLE
* pStream
= (STREAM_HANDLE
*)hUrlCacheStream
;
3359 if (dwReserved
!= 0)
3361 ERR("dwReserved != 0\n");
3362 SetLastError(ERROR_INVALID_PARAMETER
);
3366 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->lpszUrl
, INTERNET_MAX_URL_LENGTH
))
3368 SetLastError(ERROR_INVALID_HANDLE
);
3372 if (SetFilePointer(pStream
->hFile
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3374 return ReadFile(pStream
->hFile
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3377 /***********************************************************************
3378 * RetrieveUrlCacheEntryStreamA (WININET.@)
3381 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(
3382 IN LPCSTR lpszUrlName
,
3383 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3384 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
3385 IN BOOL fRandomRead
,
3389 /* NOTE: this is not the same as the way that the native
3390 * version allocates 'stream' handles. I did it this way
3391 * as it is much easier and no applications should depend
3392 * on this behaviour. (Native version appears to allocate
3393 * indices into a table)
3395 STREAM_HANDLE
* pStream
;
3398 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3399 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3401 if (!RetrieveUrlCacheEntryFileA(lpszUrlName
,
3403 lpdwCacheEntryInfoBufferSize
,
3409 hFile
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
,
3414 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
3416 if (hFile
== INVALID_HANDLE_VALUE
)
3419 /* allocate handle storage space */
3420 pStream
= heap_alloc(sizeof(STREAM_HANDLE
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3424 SetLastError(ERROR_OUTOFMEMORY
);
3428 pStream
->hFile
= hFile
;
3429 strcpy(pStream
->lpszUrl
, lpszUrlName
);
3433 /***********************************************************************
3434 * RetrieveUrlCacheEntryStreamW (WININET.@)
3437 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(
3438 IN LPCWSTR lpszUrlName
,
3439 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3440 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
3441 IN BOOL fRandomRead
,
3447 /* NOTE: this is not the same as the way that the native
3448 * version allocates 'stream' handles. I did it this way
3449 * as it is much easier and no applications should depend
3450 * on this behaviour. (Native version appears to allocate
3451 * indices into a table)
3453 STREAM_HANDLE
* pStream
;
3456 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3457 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3459 if (!RetrieveUrlCacheEntryFileW(lpszUrlName
,
3461 lpdwCacheEntryInfoBufferSize
,
3467 hFile
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
,
3472 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
3474 if (hFile
== INVALID_HANDLE_VALUE
)
3477 /* allocate handle storage space */
3478 size
= sizeof(STREAM_HANDLE
);
3479 url_len
= WideCharToMultiByte(CP_ACP
, 0, lpszUrlName
, -1, NULL
, 0, NULL
, NULL
);
3481 pStream
= heap_alloc(size
);
3485 SetLastError(ERROR_OUTOFMEMORY
);
3489 pStream
->hFile
= hFile
;
3490 WideCharToMultiByte(CP_ACP
, 0, lpszUrlName
, -1, pStream
->lpszUrl
, url_len
, NULL
, NULL
);
3494 /***********************************************************************
3495 * UnlockUrlCacheEntryStream (WININET.@)
3498 BOOL WINAPI
UnlockUrlCacheEntryStream(
3499 IN HANDLE hUrlCacheStream
,
3503 STREAM_HANDLE
* pStream
= (STREAM_HANDLE
*)hUrlCacheStream
;
3505 if (dwReserved
!= 0)
3507 ERR("dwReserved != 0\n");
3508 SetLastError(ERROR_INVALID_PARAMETER
);
3512 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->lpszUrl
, INTERNET_MAX_URL_LENGTH
))
3514 SetLastError(ERROR_INVALID_HANDLE
);
3518 if (!UnlockUrlCacheEntryFileA(pStream
->lpszUrl
, 0))
3521 CloseHandle(pStream
->hFile
);
3527 /***********************************************************************
3528 * DeleteUrlCacheEntryA (WININET.@)
3531 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3533 URLCACHECONTAINER
* pContainer
;
3534 LPURLCACHE_HEADER pHeader
;
3535 struct _HASH_ENTRY
* pHashEntry
;
3539 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3541 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
3542 if (error
!= ERROR_SUCCESS
)
3544 SetLastError(error
);
3548 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
3549 if (error
!= ERROR_SUCCESS
)
3551 SetLastError(error
);
3555 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
3558 if (!URLCache_FindHash(pHeader
, lpszUrlName
, &pHashEntry
))
3560 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3561 TRACE("entry %s not found!\n", lpszUrlName
);
3562 SetLastError(ERROR_FILE_NOT_FOUND
);
3566 ret
= DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
3568 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3573 /***********************************************************************
3574 * DeleteUrlCacheEntryW (WININET.@)
3577 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3579 URLCACHECONTAINER
* pContainer
;
3580 LPURLCACHE_HEADER pHeader
;
3581 struct _HASH_ENTRY
* pHashEntry
;
3586 TRACE("(%s)\n", debugstr_w(lpszUrlName
));
3588 urlA
= heap_strdupWtoA(lpszUrlName
);
3591 SetLastError(ERROR_OUTOFMEMORY
);
3595 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
3596 if (error
!= ERROR_SUCCESS
)
3599 SetLastError(error
);
3603 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
3604 if (error
!= ERROR_SUCCESS
)
3607 SetLastError(error
);
3611 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
3617 if (!URLCache_FindHash(pHeader
, urlA
, &pHashEntry
))
3619 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3620 TRACE("entry %s not found!\n", debugstr_a(urlA
));
3622 SetLastError(ERROR_FILE_NOT_FOUND
);
3626 ret
= DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
3628 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3633 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3635 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3639 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3641 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3645 /***********************************************************************
3646 * CreateCacheContainerA (WININET.@)
3648 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3649 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3651 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3652 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3656 /***********************************************************************
3657 * CreateCacheContainerW (WININET.@)
3659 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3660 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3662 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3663 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3667 /***********************************************************************
3668 * FindFirstUrlCacheContainerA (WININET.@)
3670 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3672 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3676 /***********************************************************************
3677 * FindFirstUrlCacheContainerW (WININET.@)
3679 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3681 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3685 /***********************************************************************
3686 * FindNextUrlCacheContainerA (WININET.@)
3688 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3690 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3694 /***********************************************************************
3695 * FindNextUrlCacheContainerW (WININET.@)
3697 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3699 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3703 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3704 LPCSTR lpszUrlSearchPattern
,
3708 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3709 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3711 LPDWORD pcbReserved2
,
3715 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3716 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3717 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3718 SetLastError(ERROR_FILE_NOT_FOUND
);
3722 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3723 LPCWSTR lpszUrlSearchPattern
,
3727 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3728 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3730 LPDWORD pcbReserved2
,
3734 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3735 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3736 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3737 SetLastError(ERROR_FILE_NOT_FOUND
);
3741 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3743 typedef struct URLCacheFindEntryHandle
3746 LPWSTR lpszUrlSearchPattern
;
3747 DWORD dwContainerIndex
;
3748 DWORD dwHashTableIndex
;
3749 DWORD dwHashEntryIndex
;
3750 } URLCacheFindEntryHandle
;
3752 /***********************************************************************
3753 * FindFirstUrlCacheEntryA (WININET.@)
3756 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3757 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3759 URLCacheFindEntryHandle
*pEntryHandle
;
3761 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3763 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3767 pEntryHandle
->dwMagic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3768 if (lpszUrlSearchPattern
)
3770 pEntryHandle
->lpszUrlSearchPattern
= heap_strdupAtoW(lpszUrlSearchPattern
);
3771 if (!pEntryHandle
->lpszUrlSearchPattern
)
3773 heap_free(pEntryHandle
);
3778 pEntryHandle
->lpszUrlSearchPattern
= NULL
;
3779 pEntryHandle
->dwContainerIndex
= 0;
3780 pEntryHandle
->dwHashTableIndex
= 0;
3781 pEntryHandle
->dwHashEntryIndex
= 0;
3783 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3785 heap_free(pEntryHandle
);
3788 return pEntryHandle
;
3791 /***********************************************************************
3792 * FindFirstUrlCacheEntryW (WININET.@)
3795 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3796 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3798 URLCacheFindEntryHandle
*pEntryHandle
;
3800 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3802 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3806 pEntryHandle
->dwMagic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3807 if (lpszUrlSearchPattern
)
3809 pEntryHandle
->lpszUrlSearchPattern
= heap_strdupW(lpszUrlSearchPattern
);
3810 if (!pEntryHandle
->lpszUrlSearchPattern
)
3812 heap_free(pEntryHandle
);
3817 pEntryHandle
->lpszUrlSearchPattern
= NULL
;
3818 pEntryHandle
->dwContainerIndex
= 0;
3819 pEntryHandle
->dwHashTableIndex
= 0;
3820 pEntryHandle
->dwHashEntryIndex
= 0;
3822 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3824 heap_free(pEntryHandle
);
3827 return pEntryHandle
;
3830 static BOOL
FindNextUrlCacheEntryInternal(
3832 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3833 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3836 URLCacheFindEntryHandle
*pEntryHandle
= (URLCacheFindEntryHandle
*)hEnumHandle
;
3837 URLCACHECONTAINER
* pContainer
;
3839 if (pEntryHandle
->dwMagic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3841 SetLastError(ERROR_INVALID_HANDLE
);
3845 for (; URLCacheContainers_Enum(pEntryHandle
->lpszUrlSearchPattern
, pEntryHandle
->dwContainerIndex
, &pContainer
);
3846 pEntryHandle
->dwContainerIndex
++, pEntryHandle
->dwHashTableIndex
= 0)
3848 LPURLCACHE_HEADER pHeader
;
3849 HASH_CACHEFILE_ENTRY
*pHashTableEntry
;
3852 error
= URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
);
3853 if (error
!= ERROR_SUCCESS
)
3855 SetLastError(error
);
3859 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
3862 for (; URLCache_EnumHashTables(pHeader
, &pEntryHandle
->dwHashTableIndex
, &pHashTableEntry
);
3863 pEntryHandle
->dwHashTableIndex
++, pEntryHandle
->dwHashEntryIndex
= 0)
3865 const struct _HASH_ENTRY
*pHashEntry
= NULL
;
3866 for (; URLCache_EnumHashTableEntries(pHeader
, pHashTableEntry
, &pEntryHandle
->dwHashEntryIndex
, &pHashEntry
);
3867 pEntryHandle
->dwHashEntryIndex
++)
3869 const URL_CACHEFILE_ENTRY
*pUrlEntry
;
3870 const CACHEFILE_ENTRY
*pEntry
= (const CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
3872 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
3875 pUrlEntry
= (const URL_CACHEFILE_ENTRY
*)pEntry
;
3876 TRACE("Found URL: %s\n",
3877 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetUrl
));
3878 TRACE("Header info: %s\n",
3879 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
));
3881 error
= URLCache_CopyEntry(
3884 lpNextCacheEntryInfo
,
3885 lpdwNextCacheEntryInfoBufferSize
,
3888 if (error
!= ERROR_SUCCESS
)
3890 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3891 SetLastError(error
);
3894 TRACE("Local File Name: %s\n",
3895 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
));
3897 /* increment the current index so that next time the function
3898 * is called the next entry is returned */
3899 pEntryHandle
->dwHashEntryIndex
++;
3900 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3905 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
3908 SetLastError(ERROR_NO_MORE_ITEMS
);
3912 /***********************************************************************
3913 * FindNextUrlCacheEntryA (WININET.@)
3915 BOOL WINAPI
FindNextUrlCacheEntryA(
3917 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3918 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3920 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3922 return FindNextUrlCacheEntryInternal(hEnumHandle
, lpNextCacheEntryInfo
,
3923 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3926 /***********************************************************************
3927 * FindNextUrlCacheEntryW (WININET.@)
3929 BOOL WINAPI
FindNextUrlCacheEntryW(
3931 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3932 LPDWORD lpdwNextCacheEntryInfoBufferSize
3935 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3937 return FindNextUrlCacheEntryInternal(hEnumHandle
,
3938 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3939 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3942 /***********************************************************************
3943 * FindCloseUrlCache (WININET.@)
3945 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3947 URLCacheFindEntryHandle
*pEntryHandle
= (URLCacheFindEntryHandle
*)hEnumHandle
;
3949 TRACE("(%p)\n", hEnumHandle
);
3951 if (!pEntryHandle
|| pEntryHandle
->dwMagic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3953 SetLastError(ERROR_INVALID_HANDLE
);
3957 pEntryHandle
->dwMagic
= 0;
3958 heap_free(pEntryHandle
->lpszUrlSearchPattern
);
3959 heap_free(pEntryHandle
);
3963 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3964 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3966 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3967 dwSearchCondition
, lpGroupId
, lpReserved
);
3971 BOOL WINAPI
FindNextUrlCacheEntryExA(
3973 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3974 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3976 LPDWORD pcbReserved2
,
3980 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3981 lpReserved
, pcbReserved2
, lpReserved3
);
3985 BOOL WINAPI
FindNextUrlCacheEntryExW(
3987 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3988 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3990 LPDWORD pcbReserved2
,
3994 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3995 lpReserved
, pcbReserved2
, lpReserved3
);
3999 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
4001 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
4005 /***********************************************************************
4006 * CreateUrlCacheGroup (WININET.@)
4009 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
4011 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
4015 /***********************************************************************
4016 * DeleteUrlCacheGroup (WININET.@)
4019 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
4021 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4022 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
4026 /***********************************************************************
4027 * SetUrlCacheEntryGroupA (WININET.@)
4030 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
4031 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
4034 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4035 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
4036 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
4037 SetLastError(ERROR_FILE_NOT_FOUND
);
4041 /***********************************************************************
4042 * SetUrlCacheEntryGroupW (WININET.@)
4045 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
4046 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
4049 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4050 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
4051 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
4052 SetLastError(ERROR_FILE_NOT_FOUND
);
4056 /***********************************************************************
4057 * GetUrlCacheConfigInfoW (WININET.@)
4059 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
4061 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
4062 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4066 /***********************************************************************
4067 * GetUrlCacheConfigInfoA (WININET.@)
4069 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
4071 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
4072 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
4076 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
4077 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
4078 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
4080 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4081 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
4082 lpdwGroupInfo
, lpReserved
);
4086 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
4087 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
4088 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
4090 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4091 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
4092 lpdwGroupInfo
, lpReserved
);
4096 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
4097 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
4099 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4100 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
4104 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
4105 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
4107 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4108 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
4112 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
4114 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
4118 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
4120 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
4124 /***********************************************************************
4125 * DeleteIE3Cache (WININET.@)
4127 * Deletes the files used by the IE3 URL caching system.
4130 * hWnd [I] A dummy window.
4131 * hInst [I] Instance of process calling the function.
4132 * lpszCmdLine [I] Options used by function.
4133 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4135 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
4137 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
4141 static BOOL
IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY
*pUrlEntry
,
4142 FILETIME
*pftLastModified
)
4145 FILETIME now
, expired
;
4147 *pftLastModified
= pUrlEntry
->LastModifiedTime
;
4148 GetSystemTimeAsFileTime(&now
);
4149 URLCache_DosDateTimeToFileTime(pUrlEntry
->wExpiredDate
,
4150 pUrlEntry
->wExpiredTime
, &expired
);
4151 /* If the expired time is 0, it's interpreted as not expired */
4152 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
4155 ret
= CompareFileTime(&expired
, &now
) < 0;
4159 /***********************************************************************
4160 * IsUrlCacheEntryExpiredA (WININET.@)
4164 * dwFlags [I] Unknown
4165 * pftLastModified [O] Last modified time
4167 BOOL WINAPI
IsUrlCacheEntryExpiredA( LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
4169 LPURLCACHE_HEADER pHeader
;
4170 struct _HASH_ENTRY
* pHashEntry
;
4171 const CACHEFILE_ENTRY
* pEntry
;
4172 const URL_CACHEFILE_ENTRY
* pUrlEntry
;
4173 URLCACHECONTAINER
* pContainer
;
4176 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
4178 if (!url
|| !pftLastModified
)
4181 FIXME("unknown flags 0x%08x\n", dwFlags
);
4183 /* Any error implies that the URL is expired, i.e. not in the cache */
4184 if (URLCacheContainers_FindContainerA(url
, &pContainer
))
4186 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4190 if (URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
))
4192 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4196 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
4198 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4202 if (!URLCache_FindHash(pHeader
, url
, &pHashEntry
))
4204 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4205 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4206 TRACE("entry %s not found!\n", url
);
4210 pEntry
= (const CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
4211 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
4213 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4214 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4215 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
4219 pUrlEntry
= (const URL_CACHEFILE_ENTRY
*)pEntry
;
4220 expired
= IsUrlCacheEntryExpiredInternal(pUrlEntry
, pftLastModified
);
4222 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4227 /***********************************************************************
4228 * IsUrlCacheEntryExpiredW (WININET.@)
4232 * dwFlags [I] Unknown
4233 * pftLastModified [O] Last modified time
4235 BOOL WINAPI
IsUrlCacheEntryExpiredW( LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
4237 LPURLCACHE_HEADER pHeader
;
4238 struct _HASH_ENTRY
* pHashEntry
;
4239 const CACHEFILE_ENTRY
* pEntry
;
4240 const URL_CACHEFILE_ENTRY
* pUrlEntry
;
4241 URLCACHECONTAINER
* pContainer
;
4244 TRACE("(%s, %08x, %p)\n", debugstr_w(url
), dwFlags
, pftLastModified
);
4246 if (!url
|| !pftLastModified
)
4249 FIXME("unknown flags 0x%08x\n", dwFlags
);
4251 /* Any error implies that the URL is expired, i.e. not in the cache */
4252 if (URLCacheContainers_FindContainerW(url
, &pContainer
))
4254 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4258 if (URLCacheContainer_OpenIndex(pContainer
, MIN_BLOCK_NO
))
4260 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4264 if (!(pHeader
= URLCacheContainer_LockIndex(pContainer
)))
4266 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4270 if (!URLCache_FindHashW(pHeader
, url
, &pHashEntry
))
4272 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4273 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4274 TRACE("entry %s not found!\n", debugstr_w(url
));
4278 if (!URLCache_FindHashW(pHeader
, url
, &pHashEntry
))
4280 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4281 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4282 TRACE("entry %s not found!\n", debugstr_w(url
));
4286 pEntry
= (const CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashEntry
->dwOffsetEntry
);
4287 if (pEntry
->dwSignature
!= URL_SIGNATURE
)
4289 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4290 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4291 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->dwSignature
, sizeof(DWORD
)));
4295 pUrlEntry
= (const URL_CACHEFILE_ENTRY
*)pEntry
;
4296 expired
= IsUrlCacheEntryExpiredInternal(pUrlEntry
, pftLastModified
);
4298 URLCacheContainer_UnlockIndex(pContainer
, pHeader
);
4303 /***********************************************************************
4304 * GetDiskInfoA (WININET.@)
4306 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
4309 ULARGE_INTEGER bytes_free
, bytes_total
;
4311 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
4315 SetLastError(ERROR_INVALID_PARAMETER
);
4319 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
4321 if (cluster_size
) *cluster_size
= 1;
4322 if (free
) *free
= bytes_free
.QuadPart
;
4323 if (total
) *total
= bytes_total
.QuadPart
;
4328 /***********************************************************************
4329 * RegisterUrlCacheNotification (WININET.@)
4331 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
4333 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
4337 /***********************************************************************
4338 * IncrementUrlCacheHeaderData (WININET.@)
4340 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
4342 FIXME("(%u, %p)\n", index
, data
);
4346 /***********************************************************************
4347 * RunOnceUrlCache (WININET.@)
4350 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4352 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4356 BOOL
init_urlcache(void)
4358 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4359 if(!dll_unload_event
)
4362 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4363 if(!free_cache_running
) {
4364 CloseHandle(dll_unload_event
);
4368 URLCacheContainers_CreateDefaults();
4372 void free_urlcache(void)
4374 SetEvent(dll_unload_event
);
4375 WaitForSingleObject(free_cache_running
, INFINITE
);
4376 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4377 CloseHandle(free_cache_running
);
4378 CloseHandle(dll_unload_event
);
4380 URLCacheContainers_DeleteAll();