wininet: Add a stub for LoadUrlCacheContent.
[wine.git] / dlls / wininet / urlcache.c
blob578473db89f4ca5294475ff1d6aeb22d92cccd9a
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
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
25 #include "config.h"
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
32 #include <ws2tcpip.h>
33 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #include <time.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wininet.h"
49 #include "winineti.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "shlwapi.h"
53 #include "shlobj.h"
54 #include "shellapi.h"
56 #include "internet.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
64 static const char urlcache_ver[] = "0.2012001";
66 #define ENTRY_START_OFFSET 0x4000
67 #define DIR_LENGTH 8
68 #define MAX_DIR_NO 0x20
69 #define BLOCKSIZE 128
70 #define HASHTABLE_SIZE 448
71 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
72 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
73 #define ALLOCATION_TABLE_OFFSET 0x250
74 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
75 #define MIN_BLOCK_NO 0x80
76 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
77 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
79 #define HASHTABLE_URL 0
80 #define HASHTABLE_DEL 1
81 #define HASHTABLE_LOCK 2
82 #define HASHTABLE_FREE 3
83 #define HASHTABLE_REDR 5
84 #define HASHTABLE_FLAG_BITS 6
86 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
87 #define INSTALLED_CACHE_ENTRY 0x10000000
88 #define GET_INSTALLED_ENTRY 0x200
89 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
91 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
93 #define FILETIME_SECOND 10000000
95 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
96 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
97 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
98 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
99 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
101 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
103 typedef struct _CACHEFILE_ENTRY
105 /* union
107 DWORD dwSignature; /* e.g. "URL " */
108 /* CHAR szSignature[4];
109 };*/
110 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
111 } CACHEFILE_ENTRY;
113 typedef struct _URL_CACHEFILE_ENTRY
115 CACHEFILE_ENTRY CacheFileEntry;
116 FILETIME LastModifiedTime;
117 FILETIME LastAccessTime;
118 WORD wExpiredDate; /* expire date in dos format */
119 WORD wExpiredTime; /* expire time in dos format */
120 DWORD dwUnknown1; /* usually zero */
121 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
122 DWORD dwUnknown2; /* usually zero */
123 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
124 DWORD dwUnknown3; /* usually 0x60 */
125 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
126 BYTE CacheDir; /* index of cache directory this url is stored in */
127 BYTE Unknown4; /* usually zero */
128 WORD wUnknown5; /* usually 0x1010 */
129 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
130 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
131 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
132 DWORD dwHeaderInfoSize;
133 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
134 WORD wLastSyncDate; /* last sync date in dos format */
135 WORD wLastSyncTime; /* last sync time in dos format */
136 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
137 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
138 WORD LastWriteDate;
139 WORD LastWriteTime;
140 DWORD dwUnknown7; /* usually zero */
141 DWORD dwUnknown8; /* usually zero */
142 /* packing to dword align start of next field */
143 /* CHAR szSourceUrlName[]; (url) */
144 /* packing to dword align start of next field */
145 /* CHAR szLocalFileName[]; (local file name excluding path) */
146 /* packing to dword align start of next field */
147 /* CHAR szHeaderInfo[]; (header info) */
148 } URL_CACHEFILE_ENTRY;
150 struct _HASH_ENTRY
152 DWORD dwHashKey;
153 DWORD dwOffsetEntry;
156 typedef struct _HASH_CACHEFILE_ENTRY
158 CACHEFILE_ENTRY CacheFileEntry;
159 DWORD dwAddressNext;
160 DWORD dwHashTableNumber;
161 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
162 } HASH_CACHEFILE_ENTRY;
164 typedef struct _DIRECTORY_DATA
166 DWORD dwNumFiles;
167 char filename[DIR_LENGTH];
168 } DIRECTORY_DATA;
170 typedef struct _URLCACHE_HEADER
172 char szSignature[28];
173 DWORD dwFileSize;
174 DWORD dwOffsetFirstHashTable;
175 DWORD dwIndexCapacityInBlocks;
176 DWORD dwBlocksInUse;
177 DWORD dwUnknown1;
178 ULARGE_INTEGER CacheLimit;
179 ULARGE_INTEGER CacheUsage;
180 ULARGE_INTEGER ExemptUsage;
181 DWORD DirectoryCount;
182 DIRECTORY_DATA directory_data[MAX_DIR_NO];
183 DWORD options[0x21];
184 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
185 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
186 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
188 typedef struct _STREAM_HANDLE
190 HANDLE hFile;
191 CHAR lpszUrl[1];
192 } STREAM_HANDLE;
194 typedef struct _URLCACHECONTAINER
196 struct list entry; /* part of a list */
197 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
198 LPWSTR path; /* path to url container directory */
199 HANDLE hMapping; /* handle of file mapping */
200 DWORD file_size; /* size of file when mapping was opened */
201 HANDLE hMutex; /* handle of mutex */
202 DWORD default_entry_type;
203 } URLCACHECONTAINER;
206 /* List of all containers available */
207 static struct list UrlContainers = LIST_INIT(UrlContainers);
209 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
211 /***********************************************************************
212 * URLCache_PathToObjectName (Internal)
214 * Converts a path to a name suitable for use as a Win32 object name.
215 * Replaces '\\' characters in-place with the specified character
216 * (usually '_' or '!')
218 * RETURNS
219 * nothing
222 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
224 for (; *lpszPath; lpszPath++)
226 if (*lpszPath == '\\')
227 *lpszPath = replace;
231 /***********************************************************************
232 * URLCacheContainer_OpenIndex (Internal)
234 * Opens the index file and saves mapping handle in hCacheIndexMapping
236 * RETURNS
237 * ERROR_SUCCESS if succeeded
238 * Any other Win32 error code if failed
241 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer, DWORD blocks_no)
243 HANDLE hFile;
244 WCHAR wszFilePath[MAX_PATH];
245 DWORD dwFileSize, new_file_size;
247 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
248 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
250 WaitForSingleObject(pContainer->hMutex, INFINITE);
252 if (pContainer->hMapping) {
253 ReleaseMutex(pContainer->hMutex);
254 return ERROR_SUCCESS;
257 strcpyW(wszFilePath, pContainer->path);
258 strcatW(wszFilePath, wszIndex);
260 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 /* Maybe the directory wasn't there? Try to create it */
264 if (CreateDirectoryW(pContainer->path, 0))
265 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
267 if (hFile == INVALID_HANDLE_VALUE)
269 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
270 ReleaseMutex(pContainer->hMutex);
271 return GetLastError();
274 dwFileSize = GetFileSize(hFile, NULL);
275 if (dwFileSize == INVALID_FILE_SIZE)
277 ReleaseMutex(pContainer->hMutex);
278 return GetLastError();
281 if (blocks_no < MIN_BLOCK_NO)
282 blocks_no = MIN_BLOCK_NO;
283 else if (blocks_no > MAX_BLOCK_NO)
284 blocks_no = MAX_BLOCK_NO;
285 new_file_size = FILE_SIZE(blocks_no);
287 if (dwFileSize < new_file_size)
289 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
290 HKEY key;
291 char achZeroes[0x1000];
292 DWORD dwOffset;
293 DWORD dwError = ERROR_SUCCESS;
295 if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
296 dwError = GetLastError();
298 /* Write zeroes to the entire file so we can safely map it without
299 * fear of getting a SEGV because the disk is full.
301 memset(achZeroes, 0, sizeof(achZeroes));
302 for (dwOffset = dwFileSize; dwOffset<new_file_size && dwError==ERROR_SUCCESS;
303 dwOffset += sizeof(achZeroes))
305 DWORD dwWrite = sizeof(achZeroes);
306 DWORD dwWritten;
308 if (new_file_size - dwOffset < dwWrite)
309 dwWrite = new_file_size - dwOffset;
310 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
311 dwWritten != dwWrite)
313 /* If we fail to write, we need to return the error that
314 * cause the problem and also make sure the file is no
315 * longer there, if possible.
317 dwError = GetLastError();
321 if (dwError == ERROR_SUCCESS)
323 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
325 if (hMapping)
327 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
329 if (pHeader && dwFileSize)
331 pHeader->dwFileSize = new_file_size;
332 pHeader->dwIndexCapacityInBlocks = blocks_no;
334 else if (pHeader)
336 WCHAR *pwchDir;
337 WCHAR wszDirPath[MAX_PATH];
338 FILETIME ft;
339 int i, j;
340 HASH_CACHEFILE_ENTRY *pHashEntry;
342 /* First set some constants and defaults in the header */
343 memcpy(pHeader->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
344 memcpy(pHeader->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
345 pHeader->dwFileSize = new_file_size;
346 pHeader->dwIndexCapacityInBlocks = blocks_no;
347 /* 127MB - taken from default for Windows 2000 */
348 pHeader->CacheLimit.QuadPart = 0x07ff5400;
349 /* Copied from a Windows 2000 cache index */
350 pHeader->DirectoryCount = pContainer->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
352 /* If the registry has a cache size set, use the registry value */
353 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
355 DWORD dw;
356 DWORD len = sizeof(dw);
357 DWORD keytype;
359 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
360 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
361 keytype == REG_DWORD)
363 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
365 RegCloseKey(key);
368 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
370 /* Last step - create the directories */
372 strcpyW(wszDirPath, pContainer->path);
373 pwchDir = wszDirPath + strlenW(wszDirPath);
374 pwchDir[8] = 0;
376 GetSystemTimeAsFileTime(&ft);
378 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
380 pHeader->directory_data[i].dwNumFiles = 0;
381 for (j = 0;; ++j)
383 int k;
384 ULONGLONG n = ft.dwHighDateTime;
386 /* Generate a file name to attempt to create.
387 * This algorithm will create what will appear
388 * to be random and unrelated directory names
389 * of up to 9 characters in length.
391 n <<= 32;
392 n += ft.dwLowDateTime;
393 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
395 for (k = 0; k < 8; ++k)
397 int r = (n % 36);
399 /* Dividing by a prime greater than 36 helps
400 * with the appearance of randomness
402 n /= 37;
404 if (r < 10)
405 pwchDir[k] = '0' + r;
406 else
407 pwchDir[k] = 'A' + (r - 10);
410 if (CreateDirectoryW(wszDirPath, 0))
412 /* The following is OK because we generated an
413 * 8 character directory name made from characters
414 * [A-Z0-9], which are equivalent for all code
415 * pages and for UTF-16
417 for (k = 0; k < 8; ++k)
418 pHeader->directory_data[i].filename[k] = pwchDir[k];
419 break;
421 else if (j >= 255)
423 /* Give up. The most likely cause of this
424 * is a full disk, but whatever the cause
425 * is, it should be more than apparent that
426 * we won't succeed.
428 dwError = GetLastError();
429 break;
434 UnmapViewOfFile(pHeader);
436 else
438 dwError = GetLastError();
440 dwFileSize = new_file_size;
441 CloseHandle(hMapping);
443 else
445 dwError = GetLastError();
449 if (dwError)
451 CloseHandle(hFile);
452 DeleteFileW(wszFilePath);
453 ReleaseMutex(pContainer->hMutex);
454 return dwError;
459 pContainer->file_size = dwFileSize;
460 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
461 URLCache_PathToObjectName(wszFilePath, '_');
462 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
463 if (!pContainer->hMapping)
465 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
466 CloseHandle(hFile);
468 /* Validate cache index file on first open */
469 if (pContainer->hMapping && blocks_no==MIN_BLOCK_NO)
471 URLCACHE_HEADER *pHeader = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
472 if (!pHeader)
474 ERR("MapViewOfFile failed (error is %d)\n", GetLastError());
475 CloseHandle(pContainer->hMapping);
476 pContainer->hMapping = NULL;
477 ReleaseMutex(pContainer->hMutex);
478 return GetLastError();
481 if (!memcmp(pHeader->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
482 memcmp(pHeader->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
484 TRACE("detected wrong version of cache: %s, expected %s\n", pHeader->szSignature, urlcache_ver);
485 UnmapViewOfFile(pHeader);
487 FreeUrlCacheSpaceW(pContainer->path, 100, 0);
489 else
491 UnmapViewOfFile(pHeader);
495 else
497 CloseHandle(hFile);
499 if (!pContainer->hMapping)
501 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
502 ReleaseMutex(pContainer->hMutex);
503 return GetLastError();
506 ReleaseMutex(pContainer->hMutex);
508 return ERROR_SUCCESS;
511 /***********************************************************************
512 * URLCacheContainer_CloseIndex (Internal)
514 * Closes the index
516 * RETURNS
517 * nothing
520 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
522 CloseHandle(pContainer->hMapping);
523 pContainer->hMapping = NULL;
526 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix,
527 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
529 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
530 int cache_prefix_len = strlenW(cache_prefix);
532 if (!pContainer)
534 return FALSE;
537 pContainer->hMapping = NULL;
538 pContainer->file_size = 0;
539 pContainer->default_entry_type = default_entry_type;
541 pContainer->path = heap_strdupW(path);
542 if (!pContainer->path)
544 heap_free(pContainer);
545 return FALSE;
548 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
549 if (!pContainer->cache_prefix)
551 heap_free(pContainer->path);
552 heap_free(pContainer);
553 return FALSE;
556 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
558 CharLowerW(mutex_name);
559 URLCache_PathToObjectName(mutex_name, '!');
561 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
563 ERR("couldn't create mutex (error is %d)\n", GetLastError());
564 heap_free(pContainer->path);
565 heap_free(pContainer);
566 return FALSE;
569 list_add_head(&UrlContainers, &pContainer->entry);
571 return TRUE;
574 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
576 list_remove(&pContainer->entry);
578 URLCacheContainer_CloseIndex(pContainer);
579 CloseHandle(pContainer->hMutex);
580 heap_free(pContainer->path);
581 heap_free(pContainer->cache_prefix);
582 heap_free(pContainer);
585 static void URLCacheContainers_CreateDefaults(void)
587 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
588 static const WCHAR UrlPrefix[] = {0};
589 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
590 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
591 static const WCHAR CookieSuffix[] = {0};
592 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
593 static const struct
595 int nFolder; /* CSIDL_* constant */
596 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
597 const WCHAR * cache_prefix; /* prefix used to reference the container */
598 DWORD default_entry_type;
599 } DefaultContainerData[] =
601 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
602 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
603 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
605 DWORD i;
607 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
609 WCHAR wszCachePath[MAX_PATH];
610 WCHAR wszMutexName[MAX_PATH];
611 int path_len, suffix_len;
613 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
615 ERR("Couldn't get path for default container %u\n", i);
616 continue;
618 path_len = strlenW(wszCachePath);
619 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
621 if (path_len + suffix_len + 2 > MAX_PATH)
623 ERR("Path too long\n");
624 continue;
627 wszCachePath[path_len] = '\\';
628 wszCachePath[path_len+1] = 0;
630 strcpyW(wszMutexName, wszCachePath);
632 if (suffix_len)
634 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
635 wszCachePath[path_len + suffix_len + 1] = '\\';
636 wszCachePath[path_len + suffix_len + 2] = '\0';
639 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath,
640 DefaultContainerData[i].default_entry_type, wszMutexName);
644 static void URLCacheContainers_DeleteAll(void)
646 while(!list_empty(&UrlContainers))
647 URLCacheContainer_DeleteContainer(
648 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
652 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
654 URLCACHECONTAINER * pContainer;
656 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
658 if(!lpwszUrl)
659 return ERROR_INVALID_PARAMETER;
661 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
663 int prefix_len = strlenW(pContainer->cache_prefix);
664 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
666 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
667 *ppContainer = pContainer;
668 return ERROR_SUCCESS;
671 ERR("no container found\n");
672 return ERROR_FILE_NOT_FOUND;
675 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
677 LPWSTR url = NULL;
678 DWORD ret;
680 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
681 return ERROR_OUTOFMEMORY;
683 ret = URLCacheContainers_FindContainerW(url, ppContainer);
684 heap_free(url);
685 return ret;
688 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
690 DWORD i = 0;
691 URLCACHECONTAINER * pContainer;
693 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
695 /* non-NULL search pattern only returns one container ever */
696 if (lpwszSearchPattern && dwIndex > 0)
697 return FALSE;
699 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
701 if (lpwszSearchPattern)
703 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
705 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
706 *ppContainer = pContainer;
707 return TRUE;
710 else
712 if (i == dwIndex)
714 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
715 *ppContainer = pContainer;
716 return TRUE;
719 i++;
721 return FALSE;
724 /***********************************************************************
725 * URLCacheContainer_LockIndex (Internal)
727 * Locks the index for system-wide exclusive access.
729 * RETURNS
730 * Cache file header if successful
731 * NULL if failed and calls SetLastError.
733 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
735 BYTE index;
736 LPVOID pIndexData;
737 URLCACHE_HEADER * pHeader;
738 DWORD error;
740 /* acquire mutex */
741 WaitForSingleObject(pContainer->hMutex, INFINITE);
743 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
745 if (!pIndexData)
747 ReleaseMutex(pContainer->hMutex);
748 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
749 return NULL;
751 pHeader = (URLCACHE_HEADER *)pIndexData;
753 /* file has grown - we need to remap to prevent us getting
754 * access violations when we try and access beyond the end
755 * of the memory mapped file */
756 if (pHeader->dwFileSize != pContainer->file_size)
758 UnmapViewOfFile( pHeader );
759 URLCacheContainer_CloseIndex(pContainer);
760 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
761 if (error != ERROR_SUCCESS)
763 ReleaseMutex(pContainer->hMutex);
764 SetLastError(error);
765 return NULL;
767 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
769 if (!pIndexData)
771 ReleaseMutex(pContainer->hMutex);
772 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
773 return NULL;
775 pHeader = (URLCACHE_HEADER *)pIndexData;
778 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
780 for (index = 0; index < pHeader->DirectoryCount; index++)
782 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
785 return pHeader;
788 /***********************************************************************
789 * URLCacheContainer_UnlockIndex (Internal)
792 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
794 /* release mutex */
795 ReleaseMutex(pContainer->hMutex);
796 return UnmapViewOfFile(pHeader);
799 #ifndef CHAR_BIT
800 #define CHAR_BIT (8 * sizeof(CHAR))
801 #endif
803 /***********************************************************************
804 * URLCache_Allocation_BlockIsFree (Internal)
806 * Is the specified block number free?
808 * RETURNS
809 * zero if free
810 * non-zero otherwise
813 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
815 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
816 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
819 /***********************************************************************
820 * URLCache_Allocation_BlockFree (Internal)
822 * Marks the specified block as free
824 * CAUTION
825 * this function is not updating used blocks count
827 * RETURNS
828 * nothing
831 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
833 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
834 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
837 /***********************************************************************
838 * URLCache_Allocation_BlockAllocate (Internal)
840 * Marks the specified block as allocated
842 * CAUTION
843 * this function is not updating used blocks count
845 * RETURNS
846 * nothing
849 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
851 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
852 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
855 /***********************************************************************
856 * URLCache_FindFirstFreeEntry (Internal)
858 * Finds and allocates the first block of free space big enough and
859 * sets ppEntry to point to it.
861 * RETURNS
862 * ERROR_SUCCESS when free memory block was found
863 * Any other Win32 error code if the entry could not be added
866 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
868 DWORD dwBlockNumber;
869 DWORD dwFreeCounter;
870 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
872 for (dwFreeCounter = 0;
873 dwFreeCounter < dwBlocksNeeded &&
874 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
875 URLCache_Allocation_BlockIsFree(pHeader->allocation_table, dwBlockNumber + dwFreeCounter);
876 dwFreeCounter++)
877 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
879 if (dwFreeCounter == dwBlocksNeeded)
881 DWORD index;
882 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
883 for (index = 0; index < dwBlocksNeeded; index++)
884 URLCache_Allocation_BlockAllocate(pHeader->allocation_table, dwBlockNumber + index);
885 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
886 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
887 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
888 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
889 pHeader->dwBlocksInUse += dwBlocksNeeded;
890 return ERROR_SUCCESS;
894 return ERROR_HANDLE_DISK_FULL;
897 /***********************************************************************
898 * URLCache_DeleteEntry (Internal)
900 * Deletes the specified entry and frees the space allocated to it
902 * RETURNS
903 * TRUE if it succeeded
904 * FALSE if it failed
907 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
909 DWORD dwStartBlock;
910 DWORD dwBlock;
912 /* update allocation table */
913 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
914 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
915 URLCache_Allocation_BlockFree(pHeader->allocation_table, dwBlock);
917 pHeader->dwBlocksInUse -= pEntry->dwBlocksUsed;
918 return TRUE;
921 /***********************************************************************
922 * URLCache_LocalFileNameToPathW (Internal)
924 * Copies the full path to the specified buffer given the local file
925 * name and the index of the directory it is in. Always sets value in
926 * lpBufferSize to the required buffer size (in bytes).
928 * RETURNS
929 * TRUE if the buffer was big enough
930 * FALSE if the buffer was too small
933 static BOOL URLCache_LocalFileNameToPathW(
934 const URLCACHECONTAINER * pContainer,
935 LPCURLCACHE_HEADER pHeader,
936 LPCSTR szLocalFileName,
937 BYTE Directory,
938 LPWSTR wszPath,
939 LPLONG lpBufferSize)
941 LONG nRequired;
942 int path_len = strlenW(pContainer->path);
943 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
944 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
946 *lpBufferSize = 0;
947 return FALSE;
950 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
951 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
952 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
953 if (nRequired <= *lpBufferSize)
955 int dir_len;
957 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
958 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
960 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
961 wszPath[dir_len + path_len] = '\\';
962 dir_len++;
964 else
966 dir_len = 0;
968 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
969 *lpBufferSize = nRequired;
970 return TRUE;
972 *lpBufferSize = nRequired;
973 return FALSE;
976 /***********************************************************************
977 * URLCache_LocalFileNameToPathA (Internal)
979 * Copies the full path to the specified buffer given the local file
980 * name and the index of the directory it is in. Always sets value in
981 * lpBufferSize to the required buffer size.
983 * RETURNS
984 * TRUE if the buffer was big enough
985 * FALSE if the buffer was too small
988 static BOOL URLCache_LocalFileNameToPathA(
989 const URLCACHECONTAINER * pContainer,
990 LPCURLCACHE_HEADER pHeader,
991 LPCSTR szLocalFileName,
992 BYTE Directory,
993 LPSTR szPath,
994 LPLONG lpBufferSize)
996 LONG nRequired;
997 int path_len, file_name_len, dir_len;
999 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
1001 *lpBufferSize = 0;
1002 return FALSE;
1005 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1006 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1007 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1008 dir_len = DIR_LENGTH+1;
1009 else
1010 dir_len = 0;
1012 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1013 if (nRequired < *lpBufferSize)
1015 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1016 if(dir_len) {
1017 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len-1);
1018 szPath[path_len + dir_len-1] = '\\';
1020 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1021 *lpBufferSize = nRequired;
1022 return TRUE;
1024 *lpBufferSize = nRequired;
1025 return FALSE;
1028 /* Just like FileTimeToDosDateTime, except that it also maps the special
1029 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1031 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1032 WORD *fattime)
1034 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1035 *fatdate = *fattime = 0;
1036 else
1037 FileTimeToDosDateTime(ft, fatdate, fattime);
1040 /***********************************************************************
1041 * URLCache_DeleteFile (Internal)
1043 static DWORD URLCache_DeleteFile(const URLCACHECONTAINER *container,
1044 URLCACHE_HEADER *header, URL_CACHEFILE_ENTRY *url_entry)
1046 WIN32_FILE_ATTRIBUTE_DATA attr;
1047 WCHAR path[MAX_PATH];
1048 LONG path_size = sizeof(path);
1049 DWORD err;
1050 WORD date, time;
1052 if(!url_entry->dwOffsetLocalName)
1053 goto succ;
1055 if(!URLCache_LocalFileNameToPathW(container, header,
1056 (LPCSTR)url_entry+url_entry->dwOffsetLocalName,
1057 url_entry->CacheDir, path, &path_size))
1058 goto succ;
1060 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1061 goto succ;
1062 URLCache_FileTimeToDosDateTime(&attr.ftLastWriteTime, &date, &time);
1063 if(date != url_entry->LastWriteDate || time != url_entry->LastWriteTime)
1064 goto succ;
1066 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1067 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1068 return err;
1070 succ:
1071 if (url_entry->CacheDir < header->DirectoryCount)
1073 if (header->directory_data[url_entry->CacheDir].dwNumFiles)
1074 header->directory_data[url_entry->CacheDir].dwNumFiles--;
1076 if (url_entry->CacheEntryType & STICKY_CACHE_ENTRY)
1078 if (url_entry->size.QuadPart < header->ExemptUsage.QuadPart)
1079 header->ExemptUsage.QuadPart -= url_entry->size.QuadPart;
1080 else
1081 header->ExemptUsage.QuadPart = 0;
1083 else
1085 if (url_entry->size.QuadPart < header->CacheUsage.QuadPart)
1086 header->CacheUsage.QuadPart -= url_entry->size.QuadPart;
1087 else
1088 header->CacheUsage.QuadPart = 0;
1091 return ERROR_SUCCESS;
1094 static BOOL urlcache_clean_leaked_entries(URLCACHECONTAINER *container, URLCACHE_HEADER *header)
1096 DWORD *leak_off;
1097 BOOL freed = FALSE;
1099 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1100 while(*leak_off) {
1101 URL_CACHEFILE_ENTRY *url_entry = (URL_CACHEFILE_ENTRY*)((LPBYTE)header + *leak_off);
1103 if(SUCCEEDED(URLCache_DeleteFile(container, header, url_entry))) {
1104 *leak_off = url_entry->dwExemptDelta;
1105 URLCache_DeleteEntry(header, &url_entry->CacheFileEntry);
1106 freed = TRUE;
1107 }else {
1108 leak_off = &url_entry->dwExemptDelta;
1112 return freed;
1115 /***********************************************************************
1116 * URLCacheContainer_CleanIndex (Internal)
1118 * This function is meant to make place in index file by removing leaked
1119 * files entries and resizing the file.
1121 * CAUTION: file view may get mapped to new memory
1123 * RETURNS
1124 * ERROR_SUCCESS when new memory is available
1125 * error code otherwise
1127 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
1129 URLCACHE_HEADER *header = *file_view;
1130 DWORD ret;
1132 TRACE("(%s %s)\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
1134 if(urlcache_clean_leaked_entries(container, header))
1135 return ERROR_SUCCESS;
1137 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1138 WARN("index file has maximal size\n");
1139 return ERROR_NOT_ENOUGH_MEMORY;
1142 URLCacheContainer_CloseIndex(container);
1143 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
1144 if(ret != ERROR_SUCCESS)
1145 return ret;
1146 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
1147 if(!header)
1148 return GetLastError();
1150 UnmapViewOfFile(*file_view);
1151 *file_view = header;
1152 return ERROR_SUCCESS;
1155 /* Just like DosDateTimeToFileTime, except that it also maps the special
1156 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1158 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
1159 FILETIME *ft)
1161 if (!fatdate && !fattime)
1162 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1163 else
1164 DosDateTimeToFileTime(fatdate, fattime, ft);
1167 /***********************************************************************
1168 * URLCache_CopyEntry (Internal)
1170 * Copies an entry from the cache index file to the Win32 structure
1172 * RETURNS
1173 * ERROR_SUCCESS if the buffer was big enough
1174 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1177 static DWORD URLCache_CopyEntry(
1178 URLCACHECONTAINER * pContainer,
1179 LPCURLCACHE_HEADER pHeader,
1180 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1181 LPDWORD lpdwBufferSize,
1182 const URL_CACHEFILE_ENTRY * pUrlEntry,
1183 BOOL bUnicode)
1185 int lenUrl;
1186 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1188 if (*lpdwBufferSize >= dwRequiredSize)
1190 lpCacheEntryInfo->lpHeaderInfo = NULL;
1191 lpCacheEntryInfo->lpszFileExtension = NULL;
1192 lpCacheEntryInfo->lpszLocalFileName = NULL;
1193 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1194 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1195 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1196 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1197 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1198 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1199 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1200 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1201 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1202 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1203 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1204 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1205 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1206 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1207 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1210 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1211 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1212 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1213 if (bUnicode)
1214 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1215 else
1216 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1217 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1219 /* FIXME: is source url optional? */
1220 if (*lpdwBufferSize >= dwRequiredSize)
1222 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1224 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1225 if (bUnicode)
1226 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1227 else
1228 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1231 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1232 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1233 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1235 if (pUrlEntry->dwOffsetLocalName)
1237 LONG nLocalFilePathSize;
1238 LPSTR lpszLocalFileName;
1239 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1240 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1241 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1242 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1244 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1246 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1248 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1249 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1250 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1252 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1254 if (*lpdwBufferSize >= dwRequiredSize)
1256 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1257 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1258 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1260 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1261 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1262 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1264 if (pUrlEntry->dwOffsetFileExtension)
1266 int lenExtension;
1268 if (bUnicode)
1269 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1270 else
1271 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1272 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1274 if (*lpdwBufferSize >= dwRequiredSize)
1276 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1277 if (bUnicode)
1278 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1279 else
1280 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1283 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1284 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1285 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1288 if (dwRequiredSize > *lpdwBufferSize)
1290 *lpdwBufferSize = dwRequiredSize;
1291 return ERROR_INSUFFICIENT_BUFFER;
1293 *lpdwBufferSize = dwRequiredSize;
1294 return ERROR_SUCCESS;
1297 /***********************************************************************
1298 * URLCache_SetEntryInfo (Internal)
1300 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1301 * according to the flags set by dwFieldControl.
1303 * RETURNS
1304 * ERROR_SUCCESS if the buffer was big enough
1305 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1308 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1310 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1311 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1312 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1313 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1314 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1315 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1316 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1317 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1318 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1319 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1320 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1321 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1322 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1323 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1324 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1325 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1327 return ERROR_SUCCESS;
1330 /***********************************************************************
1331 * URLCache_HashKey (Internal)
1333 * Returns the hash key for a given string
1335 * RETURNS
1336 * hash key for the string
1339 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1341 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1342 * but the algorithm and result are not the same!
1344 static const unsigned char lookupTable[256] =
1346 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1347 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1348 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1349 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1350 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1351 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1352 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1353 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1354 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1355 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1356 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1357 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1358 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1359 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1360 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1361 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1362 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1363 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1364 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1365 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1366 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1367 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1368 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1369 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1370 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1371 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1372 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1373 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1374 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1375 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1376 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1377 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1379 BYTE key[4];
1380 DWORD i;
1382 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1383 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1385 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1387 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1388 key[i] = lookupTable[*lpszKey ^ key[i]];
1391 return *(DWORD *)key;
1394 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1396 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1399 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1401 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1402 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1403 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1406 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1408 /* structure of hash table:
1409 * 448 entries divided into 64 blocks
1410 * each block therefore contains a chain of 7 key/offset pairs
1411 * how position in table is calculated:
1412 * 1. the url is hashed in helper function
1413 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1414 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1416 * note:
1417 * there can be multiple hash tables in the file and the offset to
1418 * the next one is stored in the header of the hash table
1420 DWORD key = URLCache_HashKey(lpszUrl);
1421 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1422 HASH_CACHEFILE_ENTRY * pHashEntry;
1423 DWORD dwHashTableNumber = 0;
1425 key >>= HASHTABLE_FLAG_BITS;
1427 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1428 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1429 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1431 int i;
1432 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1434 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1435 continue;
1437 /* make sure that it is in fact a hash entry */
1438 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1440 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1441 continue;
1444 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1446 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1447 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1449 /* FIXME: we should make sure that this is the right element
1450 * before returning and claiming that it is. We can do this
1451 * by doing a simple compare between the URL we were given
1452 * and the URL stored in the entry. However, this assumes
1453 * we know the format of all the entries stored in the
1454 * hash table */
1455 *ppHashEntry = pHashElement;
1456 return TRUE;
1460 return FALSE;
1463 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1465 LPSTR urlA;
1466 BOOL ret;
1468 urlA = heap_strdupWtoA(lpszUrl);
1469 if (!urlA)
1471 SetLastError(ERROR_OUTOFMEMORY);
1472 return FALSE;
1475 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1476 heap_free(urlA);
1477 return ret;
1480 /***********************************************************************
1481 * URLCache_HashEntrySetFlags (Internal)
1483 * Sets special bits in hash key
1485 * RETURNS
1486 * nothing
1489 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1491 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1494 /***********************************************************************
1495 * URLCache_DeleteEntryFromHash (Internal)
1497 * Searches all the hash tables in the index for the given URL and
1498 * then if found deletes the entry.
1500 * RETURNS
1501 * TRUE if the entry was found
1502 * FALSE if the entry could not be found
1505 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1507 pHashEntry->dwHashKey = HASHTABLE_DEL;
1508 return TRUE;
1511 /***********************************************************************
1512 * URLCache_AddEntryToHash (Internal)
1514 * Searches all the hash tables for a free slot based on the offset
1515 * generated from the hash key. If a free slot is found, the offset and
1516 * key are entered into the hash table.
1518 * RETURNS
1519 * ERROR_SUCCESS if the entry was added
1520 * Any other Win32 error code if the entry could not be added
1523 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1525 /* see URLCache_FindEntryInHash for structure of hash tables */
1527 DWORD key = URLCache_HashKey(lpszUrl);
1528 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1529 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1530 DWORD dwHashTableNumber = 0;
1531 DWORD error;
1533 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1535 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1536 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1537 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1539 int i;
1540 pHashPrev = pHashEntry;
1542 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1544 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1545 break;
1547 /* make sure that it is in fact a hash entry */
1548 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1550 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1551 break;
1554 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1556 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1557 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1559 pHashElement->dwHashKey = key;
1560 pHashElement->dwOffsetEntry = dwOffsetEntry;
1561 return ERROR_SUCCESS;
1565 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1566 if (error != ERROR_SUCCESS)
1567 return error;
1569 pHashEntry->HashTable[offset].dwHashKey = key;
1570 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1571 return ERROR_SUCCESS;
1574 /***********************************************************************
1575 * URLCache_CreateHashTable (Internal)
1577 * Creates a new hash table in free space and adds it to the chain of existing
1578 * hash tables.
1580 * RETURNS
1581 * ERROR_SUCCESS if the hash table was created
1582 * ERROR_DISK_FULL if the hash table could not be created
1585 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1587 DWORD dwOffset, error;
1588 int i;
1590 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1591 return error;
1593 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1595 if (pPrevHash)
1596 pPrevHash->dwAddressNext = dwOffset;
1597 else
1598 pHeader->dwOffsetFirstHashTable = dwOffset;
1599 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1600 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1601 (*ppHash)->dwAddressNext = 0;
1602 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1603 for (i = 0; i < HASHTABLE_SIZE; i++)
1605 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1606 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1608 return ERROR_SUCCESS;
1611 /***********************************************************************
1612 * URLCache_EnumHashTables (Internal)
1614 * Enumerates the hash tables in a container.
1616 * RETURNS
1617 * TRUE if an entry was found
1618 * FALSE if there are no more tables to enumerate.
1621 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1623 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1624 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1625 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1627 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1628 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1629 continue;
1630 /* make sure that it is in fact a hash entry */
1631 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1633 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1634 (*pdwHashTableNumber)++;
1635 continue;
1638 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1639 return TRUE;
1641 return FALSE;
1644 /***********************************************************************
1645 * URLCache_EnumHashTableEntries (Internal)
1647 * Enumerates entries in a hash table and returns the next non-free entry.
1649 * RETURNS
1650 * TRUE if an entry was found
1651 * FALSE if the hash table is empty or there are no more entries to
1652 * enumerate.
1655 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1656 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1658 for (; *index < HASHTABLE_SIZE ; (*index)++)
1660 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1661 continue;
1663 *ppHashEntry = &pHashEntry->HashTable[*index];
1664 TRACE("entry found %d\n", *index);
1665 return TRUE;
1667 TRACE("no more entries (%d)\n", *index);
1668 return FALSE;
1671 /***********************************************************************
1672 * URLCache_DeleteCacheDirectory (Internal)
1674 * Erase a directory containing an URL cache.
1676 * RETURNS
1677 * TRUE success, FALSE failure/aborted.
1680 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1682 DWORD path_len;
1683 WCHAR path[MAX_PATH + 1];
1684 SHFILEOPSTRUCTW shfos;
1685 int ret;
1687 path_len = strlenW(lpszPath);
1688 if (path_len >= MAX_PATH)
1689 return FALSE;
1690 strcpyW(path, lpszPath);
1691 path[path_len + 1] = 0; /* double-NUL-terminate path */
1693 shfos.hwnd = NULL;
1694 shfos.wFunc = FO_DELETE;
1695 shfos.pFrom = path;
1696 shfos.pTo = NULL;
1697 shfos.fFlags = FOF_NOCONFIRMATION;
1698 shfos.fAnyOperationsAborted = FALSE;
1699 ret = SHFileOperationW(&shfos);
1700 if (ret)
1701 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1702 return !(ret || shfos.fAnyOperationsAborted);
1705 /***********************************************************************
1706 * URLCache_IsLocked (Internal)
1708 * Checks if entry is locked. Unlocks it if possible.
1710 static BOOL URLCache_IsLocked(struct _HASH_ENTRY *hash_entry, URL_CACHEFILE_ENTRY *url_entry)
1712 FILETIME cur_time;
1713 ULARGE_INTEGER acc_time, time;
1715 if ((hash_entry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1716 return FALSE;
1718 GetSystemTimeAsFileTime(&cur_time);
1719 time.u.LowPart = cur_time.dwLowDateTime;
1720 time.u.HighPart = cur_time.dwHighDateTime;
1722 acc_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
1723 acc_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
1725 time.QuadPart -= acc_time.QuadPart;
1727 /* check if entry was locked for at least a day */
1728 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1729 URLCache_HashEntrySetFlags(hash_entry, HASHTABLE_URL);
1730 url_entry->dwUseCount = 0;
1731 return FALSE;
1734 return TRUE;
1737 /***********************************************************************
1738 * GetUrlCacheEntryInfoExA (WININET.@)
1741 BOOL WINAPI GetUrlCacheEntryInfoExA(
1742 LPCSTR lpszUrl,
1743 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1744 LPDWORD lpdwCacheEntryInfoBufSize,
1745 LPSTR lpszReserved,
1746 LPDWORD lpdwReserved,
1747 LPVOID lpReserved,
1748 DWORD dwFlags)
1750 LPURLCACHE_HEADER pHeader;
1751 struct _HASH_ENTRY * pHashEntry;
1752 const CACHEFILE_ENTRY * pEntry;
1753 const URL_CACHEFILE_ENTRY * pUrlEntry;
1754 URLCACHECONTAINER * pContainer;
1755 DWORD error;
1757 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1758 debugstr_a(lpszUrl),
1759 lpCacheEntryInfo,
1760 lpdwCacheEntryInfoBufSize,
1761 lpszReserved,
1762 lpdwReserved,
1763 lpReserved,
1764 dwFlags);
1766 if ((lpszReserved != NULL) ||
1767 (lpdwReserved != NULL) ||
1768 (lpReserved != NULL))
1770 ERR("Reserved value was not 0\n");
1771 SetLastError(ERROR_INVALID_PARAMETER);
1772 return FALSE;
1774 if (dwFlags & ~GET_INSTALLED_ENTRY)
1775 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1777 error = URLCacheContainers_FindContainerA(lpszUrl, &pContainer);
1778 if (error != ERROR_SUCCESS)
1780 SetLastError(error);
1781 return FALSE;
1784 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1785 if (error != ERROR_SUCCESS)
1787 SetLastError(error);
1788 return FALSE;
1791 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1792 return FALSE;
1794 if (!URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1796 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1797 WARN("entry %s not found!\n", debugstr_a(lpszUrl));
1798 SetLastError(ERROR_FILE_NOT_FOUND);
1799 return FALSE;
1802 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1803 if (pEntry->dwSignature != URL_SIGNATURE)
1805 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1806 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1807 SetLastError(ERROR_FILE_NOT_FOUND);
1808 return FALSE;
1811 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1812 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1813 if (pUrlEntry->dwOffsetHeaderInfo)
1814 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1816 if((dwFlags & GET_INSTALLED_ENTRY) && !(pUrlEntry->CacheEntryType & INSTALLED_CACHE_ENTRY))
1818 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1819 SetLastError(ERROR_FILE_NOT_FOUND);
1820 return FALSE;
1823 if (lpdwCacheEntryInfoBufSize)
1825 if (!lpCacheEntryInfo)
1826 *lpdwCacheEntryInfoBufSize = 0;
1828 error = URLCache_CopyEntry(
1829 pContainer,
1830 pHeader,
1831 lpCacheEntryInfo,
1832 lpdwCacheEntryInfoBufSize,
1833 pUrlEntry,
1834 FALSE /* ANSI */);
1835 if (error != ERROR_SUCCESS)
1837 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1838 SetLastError(error);
1839 return FALSE;
1841 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1844 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1846 return TRUE;
1849 /***********************************************************************
1850 * GetUrlCacheEntryInfoA (WININET.@)
1853 BOOL WINAPI GetUrlCacheEntryInfoA(
1854 IN LPCSTR lpszUrlName,
1855 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1856 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1859 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1860 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1863 /***********************************************************************
1864 * GetUrlCacheEntryInfoW (WININET.@)
1867 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1868 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1869 LPDWORD lpdwCacheEntryInfoBufferSize)
1871 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1872 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1875 /***********************************************************************
1876 * GetUrlCacheEntryInfoExW (WININET.@)
1879 BOOL WINAPI GetUrlCacheEntryInfoExW(
1880 LPCWSTR lpszUrl,
1881 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1882 LPDWORD lpdwCacheEntryInfoBufSize,
1883 LPWSTR lpszReserved,
1884 LPDWORD lpdwReserved,
1885 LPVOID lpReserved,
1886 DWORD dwFlags)
1888 LPURLCACHE_HEADER pHeader;
1889 struct _HASH_ENTRY * pHashEntry;
1890 const CACHEFILE_ENTRY * pEntry;
1891 const URL_CACHEFILE_ENTRY * pUrlEntry;
1892 URLCACHECONTAINER * pContainer;
1893 DWORD error;
1895 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1896 debugstr_w(lpszUrl),
1897 lpCacheEntryInfo,
1898 lpdwCacheEntryInfoBufSize,
1899 lpszReserved,
1900 lpdwReserved,
1901 lpReserved,
1902 dwFlags);
1904 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1905 dwFlags &= ~GET_INSTALLED_ENTRY;
1907 if ((lpszReserved != NULL) ||
1908 (lpdwReserved != NULL) ||
1909 (lpReserved != NULL))
1911 ERR("Reserved value was not 0\n");
1912 SetLastError(ERROR_INVALID_PARAMETER);
1913 return FALSE;
1915 if (dwFlags)
1916 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1918 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1919 if (error != ERROR_SUCCESS)
1921 SetLastError(error);
1922 return FALSE;
1925 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1926 if (error != ERROR_SUCCESS)
1928 SetLastError(error);
1929 return FALSE;
1932 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1933 return FALSE;
1935 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1937 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1938 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1939 SetLastError(ERROR_FILE_NOT_FOUND);
1940 return FALSE;
1943 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1944 if (pEntry->dwSignature != URL_SIGNATURE)
1946 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1947 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1948 SetLastError(ERROR_FILE_NOT_FOUND);
1949 return FALSE;
1952 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1953 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1954 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1956 if (lpdwCacheEntryInfoBufSize)
1958 if (!lpCacheEntryInfo)
1959 *lpdwCacheEntryInfoBufSize = 0;
1961 error = URLCache_CopyEntry(
1962 pContainer,
1963 pHeader,
1964 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1965 lpdwCacheEntryInfoBufSize,
1966 pUrlEntry,
1967 TRUE /* UNICODE */);
1968 if (error != ERROR_SUCCESS)
1970 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1971 SetLastError(error);
1972 return FALSE;
1974 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1977 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1979 return TRUE;
1982 /***********************************************************************
1983 * SetUrlCacheEntryInfoA (WININET.@)
1985 BOOL WINAPI SetUrlCacheEntryInfoA(
1986 LPCSTR lpszUrlName,
1987 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1988 DWORD dwFieldControl)
1990 LPURLCACHE_HEADER pHeader;
1991 struct _HASH_ENTRY * pHashEntry;
1992 CACHEFILE_ENTRY * pEntry;
1993 URLCACHECONTAINER * pContainer;
1994 DWORD error;
1996 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1998 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1999 if (error != ERROR_SUCCESS)
2001 SetLastError(error);
2002 return FALSE;
2005 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2006 if (error != ERROR_SUCCESS)
2008 SetLastError(error);
2009 return FALSE;
2012 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2013 return FALSE;
2015 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2017 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2018 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2019 SetLastError(ERROR_FILE_NOT_FOUND);
2020 return FALSE;
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);
2029 return FALSE;
2032 URLCache_SetEntryInfo(
2033 (URL_CACHEFILE_ENTRY *)pEntry,
2034 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
2035 dwFieldControl);
2037 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2039 return TRUE;
2042 /***********************************************************************
2043 * SetUrlCacheEntryInfoW (WININET.@)
2045 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
2047 LPURLCACHE_HEADER pHeader;
2048 struct _HASH_ENTRY * pHashEntry;
2049 CACHEFILE_ENTRY * pEntry;
2050 URLCACHECONTAINER * pContainer;
2051 DWORD error;
2053 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
2055 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
2056 if (error != ERROR_SUCCESS)
2058 SetLastError(error);
2059 return FALSE;
2062 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2063 if (error != ERROR_SUCCESS)
2065 SetLastError(error);
2066 return FALSE;
2069 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2070 return FALSE;
2072 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
2074 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2075 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
2076 SetLastError(ERROR_FILE_NOT_FOUND);
2077 return FALSE;
2080 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2081 if (pEntry->dwSignature != URL_SIGNATURE)
2083 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2084 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2085 SetLastError(ERROR_FILE_NOT_FOUND);
2086 return FALSE;
2089 URLCache_SetEntryInfo(
2090 (URL_CACHEFILE_ENTRY *)pEntry,
2091 lpCacheEntryInfo,
2092 dwFieldControl);
2094 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2096 return TRUE;
2099 /***********************************************************************
2100 * RetrieveUrlCacheEntryFileA (WININET.@)
2103 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2104 IN LPCSTR lpszUrlName,
2105 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2106 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2107 IN DWORD dwReserved
2110 LPURLCACHE_HEADER pHeader;
2111 struct _HASH_ENTRY * pHashEntry;
2112 CACHEFILE_ENTRY * pEntry;
2113 URL_CACHEFILE_ENTRY * pUrlEntry;
2114 URLCACHECONTAINER * pContainer;
2115 DWORD error;
2117 TRACE("(%s, %p, %p, 0x%08x)\n",
2118 debugstr_a(lpszUrlName),
2119 lpCacheEntryInfo,
2120 lpdwCacheEntryInfoBufferSize,
2121 dwReserved);
2123 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2124 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2126 SetLastError(ERROR_INVALID_PARAMETER);
2127 return FALSE;
2130 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2131 if (error != ERROR_SUCCESS)
2133 SetLastError(error);
2134 return FALSE;
2137 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2138 if (error != ERROR_SUCCESS)
2140 SetLastError(error);
2141 return FALSE;
2144 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2145 return FALSE;
2147 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2149 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2150 TRACE("entry %s not found!\n", lpszUrlName);
2151 SetLastError(ERROR_FILE_NOT_FOUND);
2152 return FALSE;
2155 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2156 if (pEntry->dwSignature != URL_SIGNATURE)
2158 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2159 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2160 SetLastError(ERROR_FILE_NOT_FOUND);
2161 return FALSE;
2164 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2165 if (!pUrlEntry->dwOffsetLocalName)
2167 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2168 SetLastError(ERROR_INVALID_DATA);
2169 return FALSE;
2172 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2173 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2175 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2176 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2177 FALSE);
2178 if (error != ERROR_SUCCESS)
2180 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2181 SetLastError(error);
2182 return FALSE;
2184 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2186 pUrlEntry->dwHitRate++;
2187 pUrlEntry->dwUseCount++;
2188 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2189 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2191 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2193 return TRUE;
2196 /***********************************************************************
2197 * RetrieveUrlCacheEntryFileW (WININET.@)
2200 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2201 IN LPCWSTR lpszUrlName,
2202 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2203 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2204 IN DWORD dwReserved
2207 LPURLCACHE_HEADER pHeader;
2208 struct _HASH_ENTRY * pHashEntry;
2209 CACHEFILE_ENTRY * pEntry;
2210 URL_CACHEFILE_ENTRY * pUrlEntry;
2211 URLCACHECONTAINER * pContainer;
2212 DWORD error;
2214 TRACE("(%s, %p, %p, 0x%08x)\n",
2215 debugstr_w(lpszUrlName),
2216 lpCacheEntryInfo,
2217 lpdwCacheEntryInfoBufferSize,
2218 dwReserved);
2220 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2221 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2223 SetLastError(ERROR_INVALID_PARAMETER);
2224 return FALSE;
2227 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2228 if (error != ERROR_SUCCESS)
2230 SetLastError(error);
2231 return FALSE;
2234 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2235 if (error != ERROR_SUCCESS)
2237 SetLastError(error);
2238 return FALSE;
2241 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2242 return FALSE;
2244 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2246 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2247 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2248 SetLastError(ERROR_FILE_NOT_FOUND);
2249 return FALSE;
2252 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2253 if (pEntry->dwSignature != URL_SIGNATURE)
2255 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2256 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2257 SetLastError(ERROR_FILE_NOT_FOUND);
2258 return FALSE;
2261 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2262 if (!pUrlEntry->dwOffsetLocalName)
2264 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2265 SetLastError(ERROR_INVALID_DATA);
2266 return FALSE;
2269 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2270 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2272 error = URLCache_CopyEntry(
2273 pContainer,
2274 pHeader,
2275 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2276 lpdwCacheEntryInfoBufferSize,
2277 pUrlEntry,
2278 TRUE /* UNICODE */);
2279 if (error != ERROR_SUCCESS)
2281 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2282 SetLastError(error);
2283 return FALSE;
2285 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2287 pUrlEntry->dwHitRate++;
2288 pUrlEntry->dwUseCount++;
2289 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2290 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2292 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2294 return TRUE;
2297 static BOOL DeleteUrlCacheEntryInternal(const URLCACHECONTAINER * pContainer,
2298 LPURLCACHE_HEADER pHeader, struct _HASH_ENTRY *pHashEntry)
2300 CACHEFILE_ENTRY * pEntry;
2301 URL_CACHEFILE_ENTRY * pUrlEntry;
2303 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2304 if (pEntry->dwSignature != URL_SIGNATURE)
2306 FIXME("Trying to delete entry of unknown format %s\n",
2307 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2308 SetLastError(ERROR_FILE_NOT_FOUND);
2309 return FALSE;
2312 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2313 if(URLCache_IsLocked(pHashEntry, pUrlEntry))
2315 TRACE("Trying to delete locked entry\n");
2316 pUrlEntry->CacheEntryType |= PENDING_DELETE_CACHE_ENTRY;
2317 SetLastError(ERROR_SHARING_VIOLATION);
2318 return FALSE;
2321 if(!URLCache_DeleteFile(pContainer, pHeader, pUrlEntry))
2323 URLCache_DeleteEntry(pHeader, pEntry);
2325 else
2327 /* Add entry to leaked files list */
2328 pUrlEntry->CacheFileEntry.dwSignature = LEAK_SIGNATURE;
2329 pUrlEntry->dwExemptDelta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2330 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->dwOffsetEntry;
2333 URLCache_DeleteEntryFromHash(pHashEntry);
2334 return TRUE;
2337 static HANDLE free_cache_running;
2338 static HANDLE dll_unload_event;
2339 static DWORD WINAPI handle_full_cache_worker(void *param)
2341 FreeUrlCacheSpaceW(NULL, 20, 0);
2342 ReleaseSemaphore(free_cache_running, 1, NULL);
2343 return 0;
2346 static void handle_full_cache(void)
2348 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2349 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2350 ReleaseSemaphore(free_cache_running, 1, NULL);
2354 /* Enumerates entries in cache, allows cache unlocking between calls. */
2355 static BOOL urlcache_next_entry(URLCACHE_HEADER *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2356 struct _HASH_ENTRY **hash_entry, CACHEFILE_ENTRY **entry)
2358 HASH_CACHEFILE_ENTRY *hashtable_entry;
2360 *hash_entry = NULL;
2361 *entry = NULL;
2363 if(!*hash_table_off) {
2364 *hash_table_off = header->dwOffsetFirstHashTable;
2365 *hash_table_entry = 0;
2367 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2368 }else {
2369 if(*hash_table_off >= header->dwFileSize) {
2370 *hash_table_off = 0;
2371 return FALSE;
2374 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2377 if(hashtable_entry->CacheFileEntry.dwSignature != HASH_SIGNATURE) {
2378 *hash_table_off = 0;
2379 return FALSE;
2382 while(1) {
2383 if(*hash_table_entry >= HASHTABLE_SIZE) {
2384 *hash_table_off = hashtable_entry->dwAddressNext;
2385 if(!*hash_table_off) {
2386 *hash_table_off = 0;
2387 return FALSE;
2390 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2391 *hash_table_entry = 0;
2394 if(hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_DEL &&
2395 hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_FREE) {
2396 *hash_entry = &hashtable_entry->HashTable[*hash_table_entry];
2397 *entry = (CACHEFILE_ENTRY*)((LPBYTE)header + hashtable_entry->HashTable[*hash_table_entry].dwOffsetEntry);
2398 (*hash_table_entry)++;
2399 return TRUE;
2402 (*hash_table_entry)++;
2405 *hash_table_off = 0;
2406 return FALSE;
2409 /* Rates an urlcache entry to determine if it can be deleted.
2411 * Score 0 means that entry can safely be removed, the bigger rating
2412 * the smaller chance of entry being removed.
2413 * DWORD_MAX means that entry can't be deleted at all.
2415 * Rating system is currently not fully compatible with native implementation.
2417 static DWORD urlcache_rate_entry(URL_CACHEFILE_ENTRY *url_entry, FILETIME *cur_time)
2419 ULARGE_INTEGER time, access_time;
2420 DWORD rating;
2422 access_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
2423 access_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
2425 time.u.LowPart = cur_time->dwLowDateTime;
2426 time.u.HighPart = cur_time->dwHighDateTime;
2428 /* Don't touch entries that were added less than 10 minutes ago */
2429 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2430 return -1;
2432 if(url_entry->CacheEntryType & STICKY_CACHE_ENTRY)
2433 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->dwExemptDelta*FILETIME_SECOND)
2434 return -1;
2436 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2437 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2439 if(url_entry->dwHitRate > 100)
2440 rating += 100;
2441 else
2442 rating += url_entry->dwHitRate;
2444 return rating;
2447 static int dword_cmp(const void *p1, const void *p2)
2449 return *(const DWORD*)p1 - *(const DWORD*)p2;
2452 /***********************************************************************
2453 * FreeUrlCacheSpaceW (WININET.@)
2455 * Frees up some cache.
2457 * PARAMETERS
2458 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2459 * size [I] Percentage of the cache that should be free.
2460 * filter [I] Which entries can't be deleted (CacheEntryType)
2462 * RETURNS
2463 * TRUE success. FALSE failure.
2465 * IMPLEMENTATION
2466 * This implementation just retrieves the path of the cache directory, and
2467 * deletes its contents from the filesystem. The correct approach would
2468 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2470 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2472 URLCACHECONTAINER *container;
2473 DWORD path_len, err;
2475 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2477 if(size<1 || size>100) {
2478 SetLastError(ERROR_INVALID_PARAMETER);
2479 return FALSE;
2482 if(cache_path) {
2483 path_len = strlenW(cache_path);
2484 if(cache_path[path_len-1] == '\\')
2485 path_len--;
2486 }else {
2487 path_len = 0;
2490 if(size==100 && !filter) {
2491 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2493 /* When cache_path==NULL only clean Temporary Internet Files */
2494 if((!path_len && container->cache_prefix[0]==0) ||
2495 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2496 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2498 BOOL ret_del;
2500 WaitForSingleObject(container->hMutex, INFINITE);
2502 /* unlock, delete, recreate and lock cache */
2503 URLCacheContainer_CloseIndex(container);
2504 ret_del = URLCache_DeleteCacheDirectory(container->path);
2505 err = URLCacheContainer_OpenIndex(container, MIN_BLOCK_NO);
2507 ReleaseMutex(container->hMutex);
2508 if(!ret_del || (err != ERROR_SUCCESS))
2509 return FALSE;
2513 return TRUE;
2516 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2518 URLCACHE_HEADER *header;
2519 struct _HASH_ENTRY *hash_entry;
2520 CACHEFILE_ENTRY *entry;
2521 URL_CACHEFILE_ENTRY *url_entry;
2522 ULONGLONG desired_size, cur_size;
2523 DWORD delete_factor, hash_table_off, hash_table_entry;
2524 DWORD rate[100], rate_no;
2525 FILETIME cur_time;
2527 if((path_len || container->cache_prefix[0]!=0) &&
2528 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2529 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2530 continue;
2532 err = URLCacheContainer_OpenIndex(container, MIN_BLOCK_NO);
2533 if(err != ERROR_SUCCESS)
2534 continue;
2536 header = URLCacheContainer_LockIndex(container);
2537 if(!header)
2538 continue;
2540 urlcache_clean_leaked_entries(container, header);
2542 desired_size = header->CacheLimit.QuadPart*(100-size)/100;
2543 cur_size = header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart;
2544 if(cur_size <= desired_size)
2545 delete_factor = 0;
2546 else
2547 delete_factor = (cur_size-desired_size)*100/cur_size;
2549 if(!delete_factor) {
2550 URLCacheContainer_UnlockIndex(container, header);
2551 continue;
2554 hash_table_off = 0;
2555 hash_table_entry = 0;
2556 rate_no = 0;
2557 GetSystemTimeAsFileTime(&cur_time);
2558 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2559 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2560 if(entry->dwSignature != URL_SIGNATURE) {
2561 WARN("only url entries are currently supported\n");
2562 continue;
2565 url_entry = (URL_CACHEFILE_ENTRY*)entry;
2566 if(url_entry->CacheEntryType & filter)
2567 continue;
2569 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2570 if(rate[rate_no] != -1)
2571 rate_no++;
2574 if(!rate_no) {
2575 TRACE("nothing to delete\n");
2576 URLCacheContainer_UnlockIndex(container, header);
2577 continue;
2580 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2582 delete_factor = delete_factor*rate_no/100;
2583 delete_factor = rate[delete_factor];
2584 TRACE("deleting files with rating %d or less\n", delete_factor);
2586 hash_table_off = 0;
2587 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2588 if(entry->dwSignature != URL_SIGNATURE)
2589 continue;
2591 url_entry = (URL_CACHEFILE_ENTRY*)entry;
2592 if(url_entry->CacheEntryType & filter)
2593 continue;
2595 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2596 TRACE("deleting file: %s\n", (char*)url_entry+url_entry->dwOffsetLocalName);
2597 DeleteUrlCacheEntryInternal(container, header, hash_entry);
2599 if(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart <= desired_size)
2600 break;
2602 /* Allow other threads to use cache while cleaning */
2603 URLCacheContainer_UnlockIndex(container, header);
2604 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2605 TRACE("got dll_unload_event - finishing\n");
2606 return TRUE;
2608 Sleep(0);
2609 header = URLCacheContainer_LockIndex(container);
2613 TRACE("cache size after cleaning 0x%s/0x%s\n",
2614 wine_dbgstr_longlong(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart),
2615 wine_dbgstr_longlong(header->CacheLimit.QuadPart));
2616 URLCacheContainer_UnlockIndex(container, header);
2619 return TRUE;
2622 /***********************************************************************
2623 * FreeUrlCacheSpaceA (WININET.@)
2625 * See FreeUrlCacheSpaceW.
2627 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2629 BOOL ret = FALSE;
2630 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2631 if (lpszCachePath == NULL || path != NULL)
2632 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2633 heap_free(path);
2634 return ret;
2637 /***********************************************************************
2638 * UnlockUrlCacheEntryFileA (WININET.@)
2641 BOOL WINAPI UnlockUrlCacheEntryFileA(
2642 IN LPCSTR lpszUrlName,
2643 IN DWORD dwReserved
2646 LPURLCACHE_HEADER pHeader;
2647 struct _HASH_ENTRY * pHashEntry;
2648 CACHEFILE_ENTRY * pEntry;
2649 URL_CACHEFILE_ENTRY * pUrlEntry;
2650 URLCACHECONTAINER * pContainer;
2651 DWORD error;
2653 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2655 if (dwReserved)
2657 ERR("dwReserved != 0\n");
2658 SetLastError(ERROR_INVALID_PARAMETER);
2659 return FALSE;
2662 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2663 if (error != ERROR_SUCCESS)
2665 SetLastError(error);
2666 return FALSE;
2669 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2670 if (error != ERROR_SUCCESS)
2672 SetLastError(error);
2673 return FALSE;
2676 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2677 return FALSE;
2679 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2681 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2682 TRACE("entry %s not found!\n", lpszUrlName);
2683 SetLastError(ERROR_FILE_NOT_FOUND);
2684 return FALSE;
2687 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2688 if (pEntry->dwSignature != URL_SIGNATURE)
2690 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2691 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2692 SetLastError(ERROR_FILE_NOT_FOUND);
2693 return FALSE;
2696 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2698 if (pUrlEntry->dwUseCount == 0)
2700 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2701 return FALSE;
2703 pUrlEntry->dwUseCount--;
2704 if (!pUrlEntry->dwUseCount)
2706 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2707 if (pUrlEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)
2708 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2711 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2713 return TRUE;
2716 /***********************************************************************
2717 * UnlockUrlCacheEntryFileW (WININET.@)
2720 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2722 LPURLCACHE_HEADER pHeader;
2723 struct _HASH_ENTRY * pHashEntry;
2724 CACHEFILE_ENTRY * pEntry;
2725 URL_CACHEFILE_ENTRY * pUrlEntry;
2726 URLCACHECONTAINER * pContainer;
2727 DWORD error;
2729 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2731 if (dwReserved)
2733 ERR("dwReserved != 0\n");
2734 SetLastError(ERROR_INVALID_PARAMETER);
2735 return FALSE;
2738 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2739 if (error != ERROR_SUCCESS)
2741 SetLastError(error);
2742 return FALSE;
2745 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2746 if (error != ERROR_SUCCESS)
2748 SetLastError(error);
2749 return FALSE;
2752 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2753 return FALSE;
2755 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2757 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2758 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2759 SetLastError(ERROR_FILE_NOT_FOUND);
2760 return FALSE;
2763 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2764 if (pEntry->dwSignature != URL_SIGNATURE)
2766 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2767 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2768 SetLastError(ERROR_FILE_NOT_FOUND);
2769 return FALSE;
2772 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2774 if (pUrlEntry->dwUseCount == 0)
2776 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2777 return FALSE;
2779 pUrlEntry->dwUseCount--;
2780 if (!pUrlEntry->dwUseCount)
2781 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2783 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2785 return TRUE;
2788 /***********************************************************************
2789 * CreateUrlCacheEntryA (WININET.@)
2792 BOOL WINAPI CreateUrlCacheEntryA(
2793 IN LPCSTR lpszUrlName,
2794 IN DWORD dwExpectedFileSize,
2795 IN LPCSTR lpszFileExtension,
2796 OUT LPSTR lpszFileName,
2797 IN DWORD dwReserved
2800 WCHAR *url_name;
2801 WCHAR *file_extension = NULL;
2802 WCHAR file_name[MAX_PATH];
2803 BOOL bSuccess = FALSE;
2804 DWORD dwError = 0;
2806 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2807 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2809 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2811 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2813 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2815 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2817 bSuccess = TRUE;
2819 else
2821 dwError = GetLastError();
2824 else
2826 dwError = GetLastError();
2828 heap_free(file_extension);
2830 else
2832 dwError = GetLastError();
2834 heap_free(url_name);
2835 if (!bSuccess) SetLastError(dwError);
2837 return bSuccess;
2839 /***********************************************************************
2840 * CreateUrlCacheEntryW (WININET.@)
2843 BOOL WINAPI CreateUrlCacheEntryW(
2844 IN LPCWSTR lpszUrlName,
2845 IN DWORD dwExpectedFileSize,
2846 IN LPCWSTR lpszFileExtension,
2847 OUT LPWSTR lpszFileName,
2848 IN DWORD dwReserved
2851 URLCACHECONTAINER * pContainer;
2852 LPURLCACHE_HEADER pHeader;
2853 CHAR szFile[MAX_PATH];
2854 WCHAR szExtension[MAX_PATH];
2855 LPCWSTR lpszUrlPart;
2856 LPCWSTR lpszUrlEnd;
2857 LPCWSTR lpszFileNameExtension;
2858 LPWSTR lpszFileNameNoPath;
2859 int i;
2860 int countnoextension;
2861 BYTE CacheDir;
2862 LONG lBufferSize;
2863 BOOL bFound = FALSE;
2864 BOOL generate_name = FALSE;
2865 int count;
2866 DWORD error;
2867 HANDLE hFile;
2868 FILETIME ft;
2870 static const WCHAR szWWW[] = {'w','w','w',0};
2872 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2873 debugstr_w(lpszUrlName),
2874 dwExpectedFileSize,
2875 debugstr_w(lpszFileExtension),
2876 lpszFileName,
2877 dwReserved);
2879 if (dwReserved)
2880 FIXME("dwReserved 0x%08x\n", dwReserved);
2882 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2884 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2885 lpszUrlEnd--;
2887 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2888 if (!lpszUrlPart)
2889 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2890 if (lpszUrlPart)
2891 lpszUrlEnd = lpszUrlPart;
2893 for (lpszUrlPart = lpszUrlEnd;
2894 (lpszUrlPart >= lpszUrlName);
2895 lpszUrlPart--)
2897 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2899 bFound = TRUE;
2900 lpszUrlPart++;
2901 break;
2904 if(!bFound)
2905 lpszUrlPart++;
2907 if (!lstrcmpW(lpszUrlPart, szWWW))
2909 lpszUrlPart += lstrlenW(szWWW);
2912 count = lpszUrlEnd - lpszUrlPart;
2914 if (bFound && (count < MAX_PATH))
2916 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2917 if (!len)
2918 return FALSE;
2919 szFile[len] = '\0';
2920 while(len && szFile[--len] == '/') szFile[len] = '\0';
2922 /* FIXME: get rid of illegal characters like \, / and : */
2923 TRACE("File name: %s\n", debugstr_a(szFile));
2925 else
2927 generate_name = TRUE;
2928 szFile[0] = 0;
2931 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2932 if (error != ERROR_SUCCESS)
2934 SetLastError(error);
2935 return FALSE;
2938 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2939 if (error != ERROR_SUCCESS)
2941 SetLastError(error);
2942 return FALSE;
2945 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2946 return FALSE;
2948 if(pHeader->DirectoryCount)
2949 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2950 else
2951 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2953 lBufferSize = MAX_PATH * sizeof(WCHAR);
2954 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2956 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2957 debugstr_a(szFile), lBufferSize);
2958 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2959 return FALSE;
2962 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2964 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2965 lpszFileNameNoPath >= lpszFileName;
2966 --lpszFileNameNoPath)
2968 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2969 break;
2972 countnoextension = lstrlenW(lpszFileNameNoPath);
2973 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2974 if (lpszFileNameExtension)
2975 countnoextension -= lstrlenW(lpszFileNameExtension);
2976 *szExtension = '\0';
2978 if (lpszFileExtension)
2980 szExtension[0] = '.';
2981 lstrcpyW(szExtension+1, lpszFileExtension);
2984 for (i = 0; i<255 && !generate_name; i++)
2986 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2987 WCHAR *p;
2989 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2990 for (p = lpszFileNameNoPath + 1; *p; p++)
2992 switch (*p)
2994 case '<': case '>':
2995 case ':': case '"':
2996 case '/': case '\\':
2997 case '|': case '?':
2998 case '*':
2999 *p = '_'; break;
3000 default: break;
3003 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3005 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3006 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3007 if (hFile != INVALID_HANDLE_VALUE)
3009 CloseHandle(hFile);
3010 return TRUE;
3014 /* Try to generate random name */
3015 GetSystemTimeAsFileTime(&ft);
3016 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
3018 for(i=0; i<255; i++)
3020 int j;
3021 ULONGLONG n = ft.dwHighDateTime;
3022 n <<= 32;
3023 n += ft.dwLowDateTime;
3024 n ^= (ULONGLONG)i<<48;
3026 for(j=0; j<8; j++)
3028 int r = (n % 36);
3029 n /= 37;
3030 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
3033 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3034 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3035 if (hFile != INVALID_HANDLE_VALUE)
3037 CloseHandle(hFile);
3038 return TRUE;
3042 WARN("Could not find a unique filename\n");
3043 return FALSE;
3046 /***********************************************************************
3047 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3049 * The bug we are compensating for is that some drongo at Microsoft
3050 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3051 * As a consequence, CommitUrlCacheEntryA has been effectively
3052 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3053 * is still defined as LPCWSTR. The result (other than madness) is
3054 * that we always need to store lpHeaderInfo in CP_ACP rather than
3055 * in UTF16, and we need to avoid converting lpHeaderInfo in
3056 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3057 * result will lose data for arbitrary binary data.
3060 static BOOL CommitUrlCacheEntryInternal(
3061 IN LPCWSTR lpszUrlName,
3062 IN LPCWSTR lpszLocalFileName,
3063 IN FILETIME ExpireTime,
3064 IN FILETIME LastModifiedTime,
3065 IN DWORD CacheEntryType,
3066 IN LPBYTE lpHeaderInfo,
3067 IN DWORD dwHeaderSize,
3068 IN LPCWSTR lpszFileExtension,
3069 IN LPCWSTR lpszOriginalUrl
3072 URLCACHECONTAINER * pContainer;
3073 LPURLCACHE_HEADER pHeader;
3074 struct _HASH_ENTRY * pHashEntry;
3075 CACHEFILE_ENTRY * pEntry;
3076 URL_CACHEFILE_ENTRY * pUrlEntry;
3077 DWORD url_entry_offset;
3078 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3079 DWORD dwOffsetLocalFileName = 0;
3080 DWORD dwOffsetHeader = 0;
3081 DWORD dwOffsetFileExtension = 0;
3082 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3083 LARGE_INTEGER file_size;
3084 BYTE cDirectory;
3085 char achFile[MAX_PATH];
3086 LPSTR lpszUrlNameA = NULL;
3087 LPSTR lpszFileExtensionA = NULL;
3088 char *pchLocalFileName = 0;
3089 DWORD hit_rate = 0;
3090 DWORD exempt_delta = 0;
3091 DWORD error;
3093 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3094 debugstr_w(lpszUrlName),
3095 debugstr_w(lpszLocalFileName),
3096 CacheEntryType,
3097 lpHeaderInfo,
3098 dwHeaderSize,
3099 debugstr_w(lpszFileExtension),
3100 debugstr_w(lpszOriginalUrl));
3102 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3104 SetLastError(ERROR_INVALID_PARAMETER);
3105 return FALSE;
3107 if (lpszOriginalUrl)
3108 WARN(": lpszOriginalUrl ignored\n");
3110 memset(&file_attr, 0, sizeof(file_attr));
3111 if (lpszLocalFileName)
3113 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3114 return FALSE;
3116 file_size.u.LowPart = file_attr.nFileSizeLow;
3117 file_size.u.HighPart = file_attr.nFileSizeHigh;
3119 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3120 if (error != ERROR_SUCCESS)
3122 SetLastError(error);
3123 return FALSE;
3126 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3127 if (error != ERROR_SUCCESS)
3129 SetLastError(error);
3130 return FALSE;
3133 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3134 return FALSE;
3136 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3137 if (!lpszUrlNameA)
3139 error = GetLastError();
3140 goto cleanup;
3143 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3145 error = GetLastError();
3146 goto cleanup;
3149 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3151 URL_CACHEFILE_ENTRY *pUrlEntry = (URL_CACHEFILE_ENTRY*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3152 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3154 TRACE("Trying to overwrite locked entry\n");
3155 error = ERROR_SHARING_VIOLATION;
3156 goto cleanup;
3159 hit_rate = pUrlEntry->dwHitRate;
3160 exempt_delta = pUrlEntry->dwExemptDelta;
3161 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3164 if (pHeader->DirectoryCount)
3165 cDirectory = 0;
3166 else
3167 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3169 if (lpszLocalFileName)
3171 BOOL bFound = FALSE;
3173 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3175 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3176 error = ERROR_INVALID_PARAMETER;
3177 goto cleanup;
3180 /* skip container path prefix */
3181 lpszLocalFileName += lstrlenW(pContainer->path);
3183 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3184 pchLocalFileName = achFile;
3186 if(pHeader->DirectoryCount)
3188 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
3190 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
3192 bFound = TRUE;
3193 break;
3197 if (!bFound)
3199 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3200 error = ERROR_INVALID_PARAMETER;
3201 goto cleanup;
3204 lpszLocalFileName += DIR_LENGTH + 1;
3205 pchLocalFileName += DIR_LENGTH + 1;
3209 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3210 if (lpszLocalFileName)
3212 dwOffsetLocalFileName = dwBytesNeeded;
3213 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3215 if (lpHeaderInfo)
3217 dwOffsetHeader = dwBytesNeeded;
3218 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3220 if (lpszFileExtensionA)
3222 dwOffsetFileExtension = dwBytesNeeded;
3223 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3226 /* round up to next block */
3227 if (dwBytesNeeded % BLOCKSIZE)
3229 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3230 dwBytesNeeded += BLOCKSIZE;
3233 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3234 while (error == ERROR_HANDLE_DISK_FULL)
3236 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3237 if (error == ERROR_SUCCESS)
3238 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3240 if (error != ERROR_SUCCESS)
3241 goto cleanup;
3243 /* FindFirstFreeEntry fills in blocks used */
3244 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3245 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3246 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
3247 pUrlEntry->CacheDir = cDirectory;
3248 pUrlEntry->CacheEntryType = CacheEntryType | pContainer->default_entry_type;
3249 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
3250 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3252 /* Sticky entries have a default exempt time of one day */
3253 exempt_delta = 86400;
3255 pUrlEntry->dwExemptDelta = exempt_delta;
3256 pUrlEntry->dwHitRate = hit_rate+1;
3257 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
3258 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
3259 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
3260 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
3261 pUrlEntry->size.QuadPart = file_size.QuadPart;
3262 pUrlEntry->dwUseCount = 0;
3263 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
3264 pUrlEntry->LastModifiedTime = LastModifiedTime;
3265 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
3266 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
3267 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->LastWriteDate, &pUrlEntry->LastWriteTime);
3269 /*** Unknowns ***/
3270 pUrlEntry->dwUnknown1 = 0;
3271 pUrlEntry->dwUnknown2 = 0;
3272 pUrlEntry->dwUnknown3 = 0x60;
3273 pUrlEntry->Unknown4 = 0;
3274 pUrlEntry->wUnknown5 = 0x1010;
3275 pUrlEntry->dwUnknown7 = 0;
3276 pUrlEntry->dwUnknown8 = 0;
3279 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
3280 if (dwOffsetLocalFileName)
3281 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3282 if (dwOffsetHeader)
3283 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3284 if (dwOffsetFileExtension)
3285 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3287 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3288 while (error == ERROR_HANDLE_DISK_FULL)
3290 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3291 if (error == ERROR_SUCCESS)
3293 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
3294 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3295 url_entry_offset, HASHTABLE_URL);
3298 if (error != ERROR_SUCCESS)
3299 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
3300 else
3302 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3303 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
3304 if (CacheEntryType & STICKY_CACHE_ENTRY)
3305 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
3306 else
3307 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
3308 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
3309 pHeader->CacheLimit.QuadPart)
3310 handle_full_cache();
3313 cleanup:
3314 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3315 heap_free(lpszUrlNameA);
3316 heap_free(lpszFileExtensionA);
3318 if (error == ERROR_SUCCESS)
3319 return TRUE;
3320 else
3322 SetLastError(error);
3323 return FALSE;
3327 /***********************************************************************
3328 * CommitUrlCacheEntryA (WININET.@)
3331 BOOL WINAPI CommitUrlCacheEntryA(
3332 IN LPCSTR lpszUrlName,
3333 IN LPCSTR lpszLocalFileName,
3334 IN FILETIME ExpireTime,
3335 IN FILETIME LastModifiedTime,
3336 IN DWORD CacheEntryType,
3337 IN LPBYTE lpHeaderInfo,
3338 IN DWORD dwHeaderSize,
3339 IN LPCSTR lpszFileExtension,
3340 IN LPCSTR lpszOriginalUrl
3343 WCHAR *url_name = NULL;
3344 WCHAR *local_file_name = NULL;
3345 WCHAR *original_url = NULL;
3346 WCHAR *file_extension = NULL;
3347 BOOL bSuccess = FALSE;
3349 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3350 debugstr_a(lpszUrlName),
3351 debugstr_a(lpszLocalFileName),
3352 CacheEntryType,
3353 lpHeaderInfo,
3354 dwHeaderSize,
3355 debugstr_a(lpszFileExtension),
3356 debugstr_a(lpszOriginalUrl));
3358 url_name = heap_strdupAtoW(lpszUrlName);
3359 if (!url_name)
3360 goto cleanup;
3362 if (lpszLocalFileName)
3364 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3365 if (!local_file_name)
3366 goto cleanup;
3368 if (lpszFileExtension)
3370 file_extension = heap_strdupAtoW(lpszFileExtension);
3371 if (!file_extension)
3372 goto cleanup;
3374 if (lpszOriginalUrl)
3376 original_url = heap_strdupAtoW(lpszOriginalUrl);
3377 if (!original_url)
3378 goto cleanup;
3381 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3382 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3383 file_extension, original_url);
3385 cleanup:
3386 heap_free(original_url);
3387 heap_free(file_extension);
3388 heap_free(local_file_name);
3389 heap_free(url_name);
3390 return bSuccess;
3393 /***********************************************************************
3394 * CommitUrlCacheEntryW (WININET.@)
3397 BOOL WINAPI CommitUrlCacheEntryW(
3398 IN LPCWSTR lpszUrlName,
3399 IN LPCWSTR lpszLocalFileName,
3400 IN FILETIME ExpireTime,
3401 IN FILETIME LastModifiedTime,
3402 IN DWORD CacheEntryType,
3403 IN LPWSTR lpHeaderInfo,
3404 IN DWORD dwHeaderSize,
3405 IN LPCWSTR lpszFileExtension,
3406 IN LPCWSTR lpszOriginalUrl
3409 DWORD dwError = 0;
3410 BOOL bSuccess = FALSE;
3411 DWORD len = 0;
3412 CHAR *header_info = NULL;
3414 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3415 debugstr_w(lpszUrlName),
3416 debugstr_w(lpszLocalFileName),
3417 CacheEntryType,
3418 lpHeaderInfo,
3419 dwHeaderSize,
3420 debugstr_w(lpszFileExtension),
3421 debugstr_w(lpszOriginalUrl));
3423 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3425 if(header_info)
3426 len = strlen(header_info);
3427 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3428 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3430 bSuccess = TRUE;
3432 else
3434 dwError = GetLastError();
3436 if (header_info)
3438 heap_free(header_info);
3439 if (!bSuccess)
3440 SetLastError(dwError);
3443 return bSuccess;
3446 /***********************************************************************
3447 * ReadUrlCacheEntryStream (WININET.@)
3450 BOOL WINAPI ReadUrlCacheEntryStream(
3451 IN HANDLE hUrlCacheStream,
3452 IN DWORD dwLocation,
3453 IN OUT LPVOID lpBuffer,
3454 IN OUT LPDWORD lpdwLen,
3455 IN DWORD dwReserved
3458 /* Get handle to file from 'stream' */
3459 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3461 if (dwReserved != 0)
3463 ERR("dwReserved != 0\n");
3464 SetLastError(ERROR_INVALID_PARAMETER);
3465 return FALSE;
3468 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3470 SetLastError(ERROR_INVALID_HANDLE);
3471 return FALSE;
3474 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3475 return FALSE;
3476 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3479 /***********************************************************************
3480 * RetrieveUrlCacheEntryStreamA (WININET.@)
3483 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3484 IN LPCSTR lpszUrlName,
3485 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3486 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3487 IN BOOL fRandomRead,
3488 IN DWORD dwReserved
3491 /* NOTE: this is not the same as the way that the native
3492 * version allocates 'stream' handles. I did it this way
3493 * as it is much easier and no applications should depend
3494 * on this behaviour. (Native version appears to allocate
3495 * indices into a table)
3497 STREAM_HANDLE * pStream;
3498 HANDLE hFile;
3500 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3501 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3503 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3504 lpCacheEntryInfo,
3505 lpdwCacheEntryInfoBufferSize,
3506 dwReserved))
3508 return NULL;
3511 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3512 GENERIC_READ,
3513 FILE_SHARE_READ,
3514 NULL,
3515 OPEN_EXISTING,
3516 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3517 NULL);
3518 if (hFile == INVALID_HANDLE_VALUE)
3519 return FALSE;
3521 /* allocate handle storage space */
3522 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3523 if (!pStream)
3525 CloseHandle(hFile);
3526 SetLastError(ERROR_OUTOFMEMORY);
3527 return FALSE;
3530 pStream->hFile = hFile;
3531 strcpy(pStream->lpszUrl, lpszUrlName);
3532 return pStream;
3535 /***********************************************************************
3536 * RetrieveUrlCacheEntryStreamW (WININET.@)
3539 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3540 IN LPCWSTR lpszUrlName,
3541 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3542 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3543 IN BOOL fRandomRead,
3544 IN DWORD dwReserved
3547 DWORD size;
3548 int url_len;
3549 /* NOTE: this is not the same as the way that the native
3550 * version allocates 'stream' handles. I did it this way
3551 * as it is much easier and no applications should depend
3552 * on this behaviour. (Native version appears to allocate
3553 * indices into a table)
3555 STREAM_HANDLE * pStream;
3556 HANDLE hFile;
3558 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3559 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3561 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3562 lpCacheEntryInfo,
3563 lpdwCacheEntryInfoBufferSize,
3564 dwReserved))
3566 return NULL;
3569 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3570 GENERIC_READ,
3571 FILE_SHARE_READ,
3572 NULL,
3573 OPEN_EXISTING,
3574 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3575 NULL);
3576 if (hFile == INVALID_HANDLE_VALUE)
3577 return FALSE;
3579 /* allocate handle storage space */
3580 size = sizeof(STREAM_HANDLE);
3581 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3582 size += url_len;
3583 pStream = heap_alloc(size);
3584 if (!pStream)
3586 CloseHandle(hFile);
3587 SetLastError(ERROR_OUTOFMEMORY);
3588 return FALSE;
3591 pStream->hFile = hFile;
3592 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3593 return pStream;
3596 /***********************************************************************
3597 * UnlockUrlCacheEntryStream (WININET.@)
3600 BOOL WINAPI UnlockUrlCacheEntryStream(
3601 IN HANDLE hUrlCacheStream,
3602 IN DWORD dwReserved
3605 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3607 if (dwReserved != 0)
3609 ERR("dwReserved != 0\n");
3610 SetLastError(ERROR_INVALID_PARAMETER);
3611 return FALSE;
3614 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3616 SetLastError(ERROR_INVALID_HANDLE);
3617 return FALSE;
3620 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3621 return FALSE;
3623 CloseHandle(pStream->hFile);
3624 heap_free(pStream);
3625 return TRUE;
3629 /***********************************************************************
3630 * DeleteUrlCacheEntryA (WININET.@)
3633 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3635 URLCACHECONTAINER * pContainer;
3636 LPURLCACHE_HEADER pHeader;
3637 struct _HASH_ENTRY * pHashEntry;
3638 DWORD error;
3639 BOOL ret;
3641 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3643 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3644 if (error != ERROR_SUCCESS)
3646 SetLastError(error);
3647 return FALSE;
3650 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3651 if (error != ERROR_SUCCESS)
3653 SetLastError(error);
3654 return FALSE;
3657 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3658 return FALSE;
3660 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3662 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3663 TRACE("entry %s not found!\n", lpszUrlName);
3664 SetLastError(ERROR_FILE_NOT_FOUND);
3665 return FALSE;
3668 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3670 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3672 return ret;
3675 /***********************************************************************
3676 * DeleteUrlCacheEntryW (WININET.@)
3679 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3681 URLCACHECONTAINER * pContainer;
3682 LPURLCACHE_HEADER pHeader;
3683 struct _HASH_ENTRY * pHashEntry;
3684 LPSTR urlA;
3685 DWORD error;
3686 BOOL ret;
3688 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3690 urlA = heap_strdupWtoA(lpszUrlName);
3691 if (!urlA)
3693 SetLastError(ERROR_OUTOFMEMORY);
3694 return FALSE;
3697 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3698 if (error != ERROR_SUCCESS)
3700 heap_free(urlA);
3701 SetLastError(error);
3702 return FALSE;
3705 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3706 if (error != ERROR_SUCCESS)
3708 heap_free(urlA);
3709 SetLastError(error);
3710 return FALSE;
3713 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3715 heap_free(urlA);
3716 return FALSE;
3719 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3721 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3722 TRACE("entry %s not found!\n", debugstr_a(urlA));
3723 heap_free(urlA);
3724 SetLastError(ERROR_FILE_NOT_FOUND);
3725 return FALSE;
3728 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3730 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3731 heap_free(urlA);
3732 return ret;
3735 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3737 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3738 return TRUE;
3741 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3743 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3744 return TRUE;
3747 /***********************************************************************
3748 * CreateCacheContainerA (WININET.@)
3750 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3751 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3753 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3754 d1, d2, d3, d4, d5, d6, d7, d8);
3755 return TRUE;
3758 /***********************************************************************
3759 * CreateCacheContainerW (WININET.@)
3761 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3762 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3764 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3765 d1, d2, d3, d4, d5, d6, d7, d8);
3766 return TRUE;
3769 /***********************************************************************
3770 * FindFirstUrlCacheContainerA (WININET.@)
3772 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3774 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3775 return NULL;
3778 /***********************************************************************
3779 * FindFirstUrlCacheContainerW (WININET.@)
3781 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3783 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3784 return NULL;
3787 /***********************************************************************
3788 * FindNextUrlCacheContainerA (WININET.@)
3790 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3792 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3793 return FALSE;
3796 /***********************************************************************
3797 * FindNextUrlCacheContainerW (WININET.@)
3799 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3801 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3802 return FALSE;
3805 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3806 LPCSTR lpszUrlSearchPattern,
3807 DWORD dwFlags,
3808 DWORD dwFilter,
3809 GROUPID GroupId,
3810 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3811 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3812 LPVOID lpReserved,
3813 LPDWORD pcbReserved2,
3814 LPVOID lpReserved3
3817 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3818 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3819 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3820 SetLastError(ERROR_FILE_NOT_FOUND);
3821 return NULL;
3824 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3825 LPCWSTR lpszUrlSearchPattern,
3826 DWORD dwFlags,
3827 DWORD dwFilter,
3828 GROUPID GroupId,
3829 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3830 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3831 LPVOID lpReserved,
3832 LPDWORD pcbReserved2,
3833 LPVOID lpReserved3
3836 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3837 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3838 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3839 SetLastError(ERROR_FILE_NOT_FOUND);
3840 return NULL;
3843 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3845 typedef struct URLCacheFindEntryHandle
3847 DWORD dwMagic;
3848 LPWSTR lpszUrlSearchPattern;
3849 DWORD dwContainerIndex;
3850 DWORD dwHashTableIndex;
3851 DWORD dwHashEntryIndex;
3852 } URLCacheFindEntryHandle;
3854 /***********************************************************************
3855 * FindFirstUrlCacheEntryA (WININET.@)
3858 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3859 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3861 URLCacheFindEntryHandle *pEntryHandle;
3863 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3865 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3866 if (!pEntryHandle)
3867 return NULL;
3869 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3870 if (lpszUrlSearchPattern)
3872 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3873 if (!pEntryHandle->lpszUrlSearchPattern)
3875 heap_free(pEntryHandle);
3876 return NULL;
3879 else
3880 pEntryHandle->lpszUrlSearchPattern = NULL;
3881 pEntryHandle->dwContainerIndex = 0;
3882 pEntryHandle->dwHashTableIndex = 0;
3883 pEntryHandle->dwHashEntryIndex = 0;
3885 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3887 heap_free(pEntryHandle);
3888 return NULL;
3890 return pEntryHandle;
3893 /***********************************************************************
3894 * FindFirstUrlCacheEntryW (WININET.@)
3897 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3898 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3900 URLCacheFindEntryHandle *pEntryHandle;
3902 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3904 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3905 if (!pEntryHandle)
3906 return NULL;
3908 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3909 if (lpszUrlSearchPattern)
3911 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3912 if (!pEntryHandle->lpszUrlSearchPattern)
3914 heap_free(pEntryHandle);
3915 return NULL;
3918 else
3919 pEntryHandle->lpszUrlSearchPattern = NULL;
3920 pEntryHandle->dwContainerIndex = 0;
3921 pEntryHandle->dwHashTableIndex = 0;
3922 pEntryHandle->dwHashEntryIndex = 0;
3924 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3926 heap_free(pEntryHandle);
3927 return NULL;
3929 return pEntryHandle;
3932 static BOOL FindNextUrlCacheEntryInternal(
3933 HANDLE hEnumHandle,
3934 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3935 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3936 BOOL unicode)
3938 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3939 URLCACHECONTAINER * pContainer;
3941 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3943 SetLastError(ERROR_INVALID_HANDLE);
3944 return FALSE;
3947 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3948 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3950 LPURLCACHE_HEADER pHeader;
3951 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3952 DWORD error;
3954 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3955 if (error != ERROR_SUCCESS)
3957 SetLastError(error);
3958 return FALSE;
3961 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3962 return FALSE;
3964 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3965 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3967 const struct _HASH_ENTRY *pHashEntry = NULL;
3968 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3969 pEntryHandle->dwHashEntryIndex++)
3971 const URL_CACHEFILE_ENTRY *pUrlEntry;
3972 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3974 if (pEntry->dwSignature != URL_SIGNATURE)
3975 continue;
3977 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3978 TRACE("Found URL: %s\n",
3979 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3980 TRACE("Header info: %s\n",
3981 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3983 error = URLCache_CopyEntry(
3984 pContainer,
3985 pHeader,
3986 lpNextCacheEntryInfo,
3987 lpdwNextCacheEntryInfoBufferSize,
3988 pUrlEntry,
3989 unicode);
3990 if (error != ERROR_SUCCESS)
3992 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3993 SetLastError(error);
3994 return FALSE;
3996 TRACE("Local File Name: %s\n",
3997 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3999 /* increment the current index so that next time the function
4000 * is called the next entry is returned */
4001 pEntryHandle->dwHashEntryIndex++;
4002 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4003 return TRUE;
4007 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4010 SetLastError(ERROR_NO_MORE_ITEMS);
4011 return FALSE;
4014 /***********************************************************************
4015 * FindNextUrlCacheEntryA (WININET.@)
4017 BOOL WINAPI FindNextUrlCacheEntryA(
4018 HANDLE hEnumHandle,
4019 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
4020 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4022 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4024 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4025 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4028 /***********************************************************************
4029 * FindNextUrlCacheEntryW (WININET.@)
4031 BOOL WINAPI FindNextUrlCacheEntryW(
4032 HANDLE hEnumHandle,
4033 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4034 LPDWORD lpdwNextCacheEntryInfoBufferSize
4037 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4039 return FindNextUrlCacheEntryInternal(hEnumHandle,
4040 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4041 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4044 /***********************************************************************
4045 * FindCloseUrlCache (WININET.@)
4047 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4049 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
4051 TRACE("(%p)\n", hEnumHandle);
4053 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4055 SetLastError(ERROR_INVALID_HANDLE);
4056 return FALSE;
4059 pEntryHandle->dwMagic = 0;
4060 heap_free(pEntryHandle->lpszUrlSearchPattern);
4061 heap_free(pEntryHandle);
4062 return TRUE;
4065 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4066 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4068 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4069 dwSearchCondition, lpGroupId, lpReserved);
4070 return NULL;
4073 BOOL WINAPI FindNextUrlCacheEntryExA(
4074 HANDLE hEnumHandle,
4075 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4076 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4077 LPVOID lpReserved,
4078 LPDWORD pcbReserved2,
4079 LPVOID lpReserved3
4082 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4083 lpReserved, pcbReserved2, lpReserved3);
4084 return FALSE;
4087 BOOL WINAPI FindNextUrlCacheEntryExW(
4088 HANDLE hEnumHandle,
4089 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4090 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4091 LPVOID lpReserved,
4092 LPDWORD pcbReserved2,
4093 LPVOID lpReserved3
4096 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4097 lpReserved, pcbReserved2, lpReserved3);
4098 return FALSE;
4101 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4103 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4104 return FALSE;
4107 /***********************************************************************
4108 * CreateUrlCacheGroup (WININET.@)
4111 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4113 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4114 return FALSE;
4117 /***********************************************************************
4118 * DeleteUrlCacheGroup (WININET.@)
4121 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4123 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4124 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4125 return FALSE;
4128 /***********************************************************************
4129 * DeleteWpadCacheForNetworks (WININET.@)
4130 * Undocumented, added in IE8
4132 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
4134 FIXME("(%d) stub\n", unk1);
4135 return FALSE;
4138 /***********************************************************************
4139 * SetUrlCacheEntryGroupA (WININET.@)
4142 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4143 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4144 LPVOID lpReserved)
4146 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4147 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4148 pbGroupAttributes, cbGroupAttributes, lpReserved);
4149 SetLastError(ERROR_FILE_NOT_FOUND);
4150 return FALSE;
4153 /***********************************************************************
4154 * SetUrlCacheEntryGroupW (WININET.@)
4157 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4158 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4159 LPVOID lpReserved)
4161 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4162 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4163 pbGroupAttributes, cbGroupAttributes, lpReserved);
4164 SetLastError(ERROR_FILE_NOT_FOUND);
4165 return FALSE;
4168 /***********************************************************************
4169 * GetUrlCacheConfigInfoW (WININET.@)
4171 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4173 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4174 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4175 return FALSE;
4178 /***********************************************************************
4179 * GetUrlCacheConfigInfoA (WININET.@)
4181 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4183 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4184 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4185 return FALSE;
4188 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4189 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4190 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4192 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4193 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4194 lpdwGroupInfo, lpReserved);
4195 return FALSE;
4198 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4199 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4200 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4202 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4203 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4204 lpdwGroupInfo, lpReserved);
4205 return FALSE;
4208 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4209 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4211 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4212 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4213 return TRUE;
4216 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4217 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4219 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4220 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4221 return TRUE;
4224 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4226 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4227 return TRUE;
4230 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4232 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4233 return TRUE;
4236 /***********************************************************************
4237 * DeleteIE3Cache (WININET.@)
4239 * Deletes the files used by the IE3 URL caching system.
4241 * PARAMS
4242 * hWnd [I] A dummy window.
4243 * hInst [I] Instance of process calling the function.
4244 * lpszCmdLine [I] Options used by function.
4245 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4247 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4249 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4250 return 0;
4253 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
4254 FILETIME *pftLastModified)
4256 BOOL ret;
4257 FILETIME now, expired;
4259 *pftLastModified = pUrlEntry->LastModifiedTime;
4260 GetSystemTimeAsFileTime(&now);
4261 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
4262 pUrlEntry->wExpiredTime, &expired);
4263 /* If the expired time is 0, it's interpreted as not expired */
4264 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4265 ret = FALSE;
4266 else
4267 ret = CompareFileTime(&expired, &now) < 0;
4268 return ret;
4271 /***********************************************************************
4272 * IsUrlCacheEntryExpiredA (WININET.@)
4274 * PARAMS
4275 * url [I] Url
4276 * dwFlags [I] Unknown
4277 * pftLastModified [O] Last modified time
4279 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4281 LPURLCACHE_HEADER pHeader;
4282 struct _HASH_ENTRY * pHashEntry;
4283 const CACHEFILE_ENTRY * pEntry;
4284 const URL_CACHEFILE_ENTRY * pUrlEntry;
4285 URLCACHECONTAINER * pContainer;
4286 BOOL expired;
4288 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4290 if (!url || !pftLastModified)
4291 return TRUE;
4292 if (dwFlags)
4293 FIXME("unknown flags 0x%08x\n", dwFlags);
4295 /* Any error implies that the URL is expired, i.e. not in the cache */
4296 if (URLCacheContainers_FindContainerA(url, &pContainer))
4298 memset(pftLastModified, 0, sizeof(*pftLastModified));
4299 return TRUE;
4302 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4304 memset(pftLastModified, 0, sizeof(*pftLastModified));
4305 return TRUE;
4308 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4310 memset(pftLastModified, 0, sizeof(*pftLastModified));
4311 return TRUE;
4314 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4316 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4317 memset(pftLastModified, 0, sizeof(*pftLastModified));
4318 TRACE("entry %s not found!\n", url);
4319 return TRUE;
4322 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4323 if (pEntry->dwSignature != URL_SIGNATURE)
4325 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4326 memset(pftLastModified, 0, sizeof(*pftLastModified));
4327 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4328 return TRUE;
4331 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4332 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4334 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4336 return expired;
4339 /***********************************************************************
4340 * IsUrlCacheEntryExpiredW (WININET.@)
4342 * PARAMS
4343 * url [I] Url
4344 * dwFlags [I] Unknown
4345 * pftLastModified [O] Last modified time
4347 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4349 LPURLCACHE_HEADER pHeader;
4350 struct _HASH_ENTRY * pHashEntry;
4351 const CACHEFILE_ENTRY * pEntry;
4352 const URL_CACHEFILE_ENTRY * pUrlEntry;
4353 URLCACHECONTAINER * pContainer;
4354 BOOL expired;
4356 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4358 if (!url || !pftLastModified)
4359 return TRUE;
4360 if (dwFlags)
4361 FIXME("unknown flags 0x%08x\n", dwFlags);
4363 /* Any error implies that the URL is expired, i.e. not in the cache */
4364 if (URLCacheContainers_FindContainerW(url, &pContainer))
4366 memset(pftLastModified, 0, sizeof(*pftLastModified));
4367 return TRUE;
4370 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4372 memset(pftLastModified, 0, sizeof(*pftLastModified));
4373 return TRUE;
4376 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4378 memset(pftLastModified, 0, sizeof(*pftLastModified));
4379 return TRUE;
4382 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4384 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4385 memset(pftLastModified, 0, sizeof(*pftLastModified));
4386 TRACE("entry %s not found!\n", debugstr_w(url));
4387 return TRUE;
4390 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4392 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4393 memset(pftLastModified, 0, sizeof(*pftLastModified));
4394 TRACE("entry %s not found!\n", debugstr_w(url));
4395 return TRUE;
4398 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4399 if (pEntry->dwSignature != URL_SIGNATURE)
4401 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4402 memset(pftLastModified, 0, sizeof(*pftLastModified));
4403 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4404 return TRUE;
4407 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4408 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4410 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4412 return expired;
4415 /***********************************************************************
4416 * GetDiskInfoA (WININET.@)
4418 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4420 BOOL ret;
4421 ULARGE_INTEGER bytes_free, bytes_total;
4423 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4425 if (!path)
4427 SetLastError(ERROR_INVALID_PARAMETER);
4428 return FALSE;
4431 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4433 if (cluster_size) *cluster_size = 1;
4434 if (free) *free = bytes_free.QuadPart;
4435 if (total) *total = bytes_total.QuadPart;
4437 return ret;
4440 /***********************************************************************
4441 * RegisterUrlCacheNotification (WININET.@)
4443 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4445 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4446 return 0;
4449 /***********************************************************************
4450 * IncrementUrlCacheHeaderData (WININET.@)
4452 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4454 FIXME("(%u, %p)\n", index, data);
4455 return FALSE;
4458 /***********************************************************************
4459 * RunOnceUrlCache (WININET.@)
4462 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4464 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4465 return 0;
4468 BOOL init_urlcache(void)
4470 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4471 if(!dll_unload_event)
4472 return FALSE;
4474 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4475 if(!free_cache_running) {
4476 CloseHandle(dll_unload_event);
4477 return FALSE;
4480 URLCacheContainers_CreateDefaults();
4481 return TRUE;
4484 void free_urlcache(void)
4486 SetEvent(dll_unload_event);
4487 WaitForSingleObject(free_cache_running, INFINITE);
4488 ReleaseSemaphore(free_cache_running, 1, NULL);
4489 CloseHandle(free_cache_running);
4490 CloseHandle(dll_unload_event);
4492 URLCacheContainers_DeleteAll();
4495 /***********************************************************************
4496 * LoadUrlCacheContent (WININET.@)
4498 BOOL WINAPI LoadUrlCacheContent(void)
4500 FIXME("stub!\n");
4501 return FALSE;