Assorted spelling fixes.
[wine/multimedia.git] / dlls / wininet / urlcache.c
blob249ac46ade1aa826a365ba4aa5e484bdfd480955
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] How many percents of the cache 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 int count;
2865 DWORD error;
2866 HANDLE hFile;
2867 FILETIME ft;
2869 static const WCHAR szWWW[] = {'w','w','w',0};
2870 static const WCHAR fmt[] = {'%','0','8','X','%','s',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 (!lstrcmpW(lpszUrlPart, szWWW))
2906 lpszUrlPart += lstrlenW(szWWW);
2909 count = lpszUrlEnd - lpszUrlPart;
2911 if (bFound && (count < MAX_PATH))
2913 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2914 if (!len)
2915 return FALSE;
2916 szFile[len] = '\0';
2917 while(len && szFile[--len] == '/') szFile[len] = '\0';
2919 /* FIXME: get rid of illegal characters like \, / and : */
2921 else
2923 FIXME("need to generate a random filename\n");
2926 TRACE("File name: %s\n", debugstr_a(szFile));
2928 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2929 if (error != ERROR_SUCCESS)
2931 SetLastError(error);
2932 return FALSE;
2935 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2936 if (error != ERROR_SUCCESS)
2938 SetLastError(error);
2939 return FALSE;
2942 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2943 return FALSE;
2945 if(pHeader->DirectoryCount)
2946 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2947 else
2948 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2950 lBufferSize = MAX_PATH * sizeof(WCHAR);
2951 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2953 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2954 debugstr_a(szFile), lBufferSize);
2955 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2956 return FALSE;
2959 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2961 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2962 lpszFileNameNoPath >= lpszFileName;
2963 --lpszFileNameNoPath)
2965 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2966 break;
2969 countnoextension = lstrlenW(lpszFileNameNoPath);
2970 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2971 if (lpszFileNameExtension)
2972 countnoextension -= lstrlenW(lpszFileNameExtension);
2973 *szExtension = '\0';
2975 if (lpszFileExtension)
2977 szExtension[0] = '.';
2978 lstrcpyW(szExtension+1, lpszFileExtension);
2981 for (i = 0; i < 255; i++)
2983 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2984 WCHAR *p;
2986 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2987 for (p = lpszFileNameNoPath + 1; *p; p++)
2989 switch (*p)
2991 case '<': case '>':
2992 case ':': case '"':
2993 case '/': case '\\':
2994 case '|': case '?':
2995 case '*':
2996 *p = '_'; break;
2997 default: break;
3000 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3002 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3003 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3004 if (hFile != INVALID_HANDLE_VALUE)
3006 CloseHandle(hFile);
3007 return TRUE;
3011 GetSystemTimeAsFileTime(&ft);
3012 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
3014 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3015 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3016 if (hFile != INVALID_HANDLE_VALUE)
3018 CloseHandle(hFile);
3019 return TRUE;
3022 WARN("Could not find a unique filename\n");
3023 return FALSE;
3026 /***********************************************************************
3027 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3029 * The bug we are compensating for is that some drongo at Microsoft
3030 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3031 * As a consequence, CommitUrlCacheEntryA has been effectively
3032 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3033 * is still defined as LPCWSTR. The result (other than madness) is
3034 * that we always need to store lpHeaderInfo in CP_ACP rather than
3035 * in UTF16, and we need to avoid converting lpHeaderInfo in
3036 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3037 * result will lose data for arbitrary binary data.
3040 static BOOL CommitUrlCacheEntryInternal(
3041 IN LPCWSTR lpszUrlName,
3042 IN LPCWSTR lpszLocalFileName,
3043 IN FILETIME ExpireTime,
3044 IN FILETIME LastModifiedTime,
3045 IN DWORD CacheEntryType,
3046 IN LPBYTE lpHeaderInfo,
3047 IN DWORD dwHeaderSize,
3048 IN LPCWSTR lpszFileExtension,
3049 IN LPCWSTR lpszOriginalUrl
3052 URLCACHECONTAINER * pContainer;
3053 LPURLCACHE_HEADER pHeader;
3054 struct _HASH_ENTRY * pHashEntry;
3055 CACHEFILE_ENTRY * pEntry;
3056 URL_CACHEFILE_ENTRY * pUrlEntry;
3057 DWORD url_entry_offset;
3058 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3059 DWORD dwOffsetLocalFileName = 0;
3060 DWORD dwOffsetHeader = 0;
3061 DWORD dwOffsetFileExtension = 0;
3062 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3063 LARGE_INTEGER file_size;
3064 BYTE cDirectory;
3065 char achFile[MAX_PATH];
3066 LPSTR lpszUrlNameA = NULL;
3067 LPSTR lpszFileExtensionA = NULL;
3068 char *pchLocalFileName = 0;
3069 DWORD hit_rate = 0;
3070 DWORD exempt_delta = 0;
3071 DWORD error;
3073 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3074 debugstr_w(lpszUrlName),
3075 debugstr_w(lpszLocalFileName),
3076 CacheEntryType,
3077 lpHeaderInfo,
3078 dwHeaderSize,
3079 debugstr_w(lpszFileExtension),
3080 debugstr_w(lpszOriginalUrl));
3082 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3084 SetLastError(ERROR_INVALID_PARAMETER);
3085 return FALSE;
3087 if (lpszOriginalUrl)
3088 WARN(": lpszOriginalUrl ignored\n");
3090 memset(&file_attr, 0, sizeof(file_attr));
3091 if (lpszLocalFileName)
3093 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3094 return FALSE;
3096 file_size.u.LowPart = file_attr.nFileSizeLow;
3097 file_size.u.HighPart = file_attr.nFileSizeHigh;
3099 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3100 if (error != ERROR_SUCCESS)
3102 SetLastError(error);
3103 return FALSE;
3106 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3107 if (error != ERROR_SUCCESS)
3109 SetLastError(error);
3110 return FALSE;
3113 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3114 return FALSE;
3116 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3117 if (!lpszUrlNameA)
3119 error = GetLastError();
3120 goto cleanup;
3123 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3125 error = GetLastError();
3126 goto cleanup;
3129 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3131 URL_CACHEFILE_ENTRY *pUrlEntry = (URL_CACHEFILE_ENTRY*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3132 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3134 TRACE("Trying to overwrite locked entry\n");
3135 error = ERROR_SHARING_VIOLATION;
3136 goto cleanup;
3139 hit_rate = pUrlEntry->dwHitRate;
3140 exempt_delta = pUrlEntry->dwExemptDelta;
3141 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3144 if (pHeader->DirectoryCount)
3145 cDirectory = 0;
3146 else
3147 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3149 if (lpszLocalFileName)
3151 BOOL bFound = FALSE;
3153 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3155 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3156 error = ERROR_INVALID_PARAMETER;
3157 goto cleanup;
3160 /* skip container path prefix */
3161 lpszLocalFileName += lstrlenW(pContainer->path);
3163 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3164 pchLocalFileName = achFile;
3166 if(pHeader->DirectoryCount)
3168 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
3170 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
3172 bFound = TRUE;
3173 break;
3177 if (!bFound)
3179 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3180 error = ERROR_INVALID_PARAMETER;
3181 goto cleanup;
3184 lpszLocalFileName += DIR_LENGTH + 1;
3185 pchLocalFileName += DIR_LENGTH + 1;
3189 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3190 if (lpszLocalFileName)
3192 dwOffsetLocalFileName = dwBytesNeeded;
3193 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3195 if (lpHeaderInfo)
3197 dwOffsetHeader = dwBytesNeeded;
3198 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3200 if (lpszFileExtensionA)
3202 dwOffsetFileExtension = dwBytesNeeded;
3203 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3206 /* round up to next block */
3207 if (dwBytesNeeded % BLOCKSIZE)
3209 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3210 dwBytesNeeded += BLOCKSIZE;
3213 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3214 while (error == ERROR_HANDLE_DISK_FULL)
3216 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3217 if (error == ERROR_SUCCESS)
3218 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3220 if (error != ERROR_SUCCESS)
3221 goto cleanup;
3223 /* FindFirstFreeEntry fills in blocks used */
3224 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3225 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3226 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
3227 pUrlEntry->CacheDir = cDirectory;
3228 pUrlEntry->CacheEntryType = CacheEntryType | pContainer->default_entry_type;
3229 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
3230 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3232 /* Sticky entries have a default exempt time of one day */
3233 exempt_delta = 86400;
3235 pUrlEntry->dwExemptDelta = exempt_delta;
3236 pUrlEntry->dwHitRate = hit_rate+1;
3237 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
3238 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
3239 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
3240 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
3241 pUrlEntry->size.QuadPart = file_size.QuadPart;
3242 pUrlEntry->dwUseCount = 0;
3243 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
3244 pUrlEntry->LastModifiedTime = LastModifiedTime;
3245 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
3246 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
3247 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->LastWriteDate, &pUrlEntry->LastWriteTime);
3249 /*** Unknowns ***/
3250 pUrlEntry->dwUnknown1 = 0;
3251 pUrlEntry->dwUnknown2 = 0;
3252 pUrlEntry->dwUnknown3 = 0x60;
3253 pUrlEntry->Unknown4 = 0;
3254 pUrlEntry->wUnknown5 = 0x1010;
3255 pUrlEntry->dwUnknown7 = 0;
3256 pUrlEntry->dwUnknown8 = 0;
3259 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
3260 if (dwOffsetLocalFileName)
3261 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3262 if (dwOffsetHeader)
3263 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3264 if (dwOffsetFileExtension)
3265 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3267 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3268 while (error == ERROR_HANDLE_DISK_FULL)
3270 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3271 if (error == ERROR_SUCCESS)
3273 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
3274 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3275 url_entry_offset, HASHTABLE_URL);
3278 if (error != ERROR_SUCCESS)
3279 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
3280 else
3282 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3283 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
3284 if (CacheEntryType & STICKY_CACHE_ENTRY)
3285 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
3286 else
3287 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
3288 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
3289 pHeader->CacheLimit.QuadPart)
3290 handle_full_cache();
3293 cleanup:
3294 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3295 heap_free(lpszUrlNameA);
3296 heap_free(lpszFileExtensionA);
3298 if (error == ERROR_SUCCESS)
3299 return TRUE;
3300 else
3302 SetLastError(error);
3303 return FALSE;
3307 /***********************************************************************
3308 * CommitUrlCacheEntryA (WININET.@)
3311 BOOL WINAPI CommitUrlCacheEntryA(
3312 IN LPCSTR lpszUrlName,
3313 IN LPCSTR lpszLocalFileName,
3314 IN FILETIME ExpireTime,
3315 IN FILETIME LastModifiedTime,
3316 IN DWORD CacheEntryType,
3317 IN LPBYTE lpHeaderInfo,
3318 IN DWORD dwHeaderSize,
3319 IN LPCSTR lpszFileExtension,
3320 IN LPCSTR lpszOriginalUrl
3323 WCHAR *url_name = NULL;
3324 WCHAR *local_file_name = NULL;
3325 WCHAR *original_url = NULL;
3326 WCHAR *file_extension = NULL;
3327 BOOL bSuccess = FALSE;
3329 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3330 debugstr_a(lpszUrlName),
3331 debugstr_a(lpszLocalFileName),
3332 CacheEntryType,
3333 lpHeaderInfo,
3334 dwHeaderSize,
3335 debugstr_a(lpszFileExtension),
3336 debugstr_a(lpszOriginalUrl));
3338 url_name = heap_strdupAtoW(lpszUrlName);
3339 if (!url_name)
3340 goto cleanup;
3342 if (lpszLocalFileName)
3344 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3345 if (!local_file_name)
3346 goto cleanup;
3348 if (lpszFileExtension)
3350 file_extension = heap_strdupAtoW(lpszFileExtension);
3351 if (!file_extension)
3352 goto cleanup;
3354 if (lpszOriginalUrl)
3356 original_url = heap_strdupAtoW(lpszOriginalUrl);
3357 if (!original_url)
3358 goto cleanup;
3361 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3362 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3363 file_extension, original_url);
3365 cleanup:
3366 heap_free(original_url);
3367 heap_free(file_extension);
3368 heap_free(local_file_name);
3369 heap_free(url_name);
3370 return bSuccess;
3373 /***********************************************************************
3374 * CommitUrlCacheEntryW (WININET.@)
3377 BOOL WINAPI CommitUrlCacheEntryW(
3378 IN LPCWSTR lpszUrlName,
3379 IN LPCWSTR lpszLocalFileName,
3380 IN FILETIME ExpireTime,
3381 IN FILETIME LastModifiedTime,
3382 IN DWORD CacheEntryType,
3383 IN LPWSTR lpHeaderInfo,
3384 IN DWORD dwHeaderSize,
3385 IN LPCWSTR lpszFileExtension,
3386 IN LPCWSTR lpszOriginalUrl
3389 DWORD dwError = 0;
3390 BOOL bSuccess = FALSE;
3391 DWORD len = 0;
3392 CHAR *header_info = NULL;
3394 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3395 debugstr_w(lpszUrlName),
3396 debugstr_w(lpszLocalFileName),
3397 CacheEntryType,
3398 lpHeaderInfo,
3399 dwHeaderSize,
3400 debugstr_w(lpszFileExtension),
3401 debugstr_w(lpszOriginalUrl));
3403 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3405 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3406 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3408 bSuccess = TRUE;
3410 else
3412 dwError = GetLastError();
3414 if (header_info)
3416 heap_free(header_info);
3417 if (!bSuccess)
3418 SetLastError(dwError);
3421 return bSuccess;
3424 /***********************************************************************
3425 * ReadUrlCacheEntryStream (WININET.@)
3428 BOOL WINAPI ReadUrlCacheEntryStream(
3429 IN HANDLE hUrlCacheStream,
3430 IN DWORD dwLocation,
3431 IN OUT LPVOID lpBuffer,
3432 IN OUT LPDWORD lpdwLen,
3433 IN DWORD dwReserved
3436 /* Get handle to file from 'stream' */
3437 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3439 if (dwReserved != 0)
3441 ERR("dwReserved != 0\n");
3442 SetLastError(ERROR_INVALID_PARAMETER);
3443 return FALSE;
3446 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3448 SetLastError(ERROR_INVALID_HANDLE);
3449 return FALSE;
3452 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3453 return FALSE;
3454 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3457 /***********************************************************************
3458 * RetrieveUrlCacheEntryStreamA (WININET.@)
3461 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3462 IN LPCSTR lpszUrlName,
3463 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3464 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3465 IN BOOL fRandomRead,
3466 IN DWORD dwReserved
3469 /* NOTE: this is not the same as the way that the native
3470 * version allocates 'stream' handles. I did it this way
3471 * as it is much easier and no applications should depend
3472 * on this behaviour. (Native version appears to allocate
3473 * indices into a table)
3475 STREAM_HANDLE * pStream;
3476 HANDLE hFile;
3478 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3479 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3481 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3482 lpCacheEntryInfo,
3483 lpdwCacheEntryInfoBufferSize,
3484 dwReserved))
3486 return NULL;
3489 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3490 GENERIC_READ,
3491 FILE_SHARE_READ,
3492 NULL,
3493 OPEN_EXISTING,
3494 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3495 NULL);
3496 if (hFile == INVALID_HANDLE_VALUE)
3497 return FALSE;
3499 /* allocate handle storage space */
3500 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3501 if (!pStream)
3503 CloseHandle(hFile);
3504 SetLastError(ERROR_OUTOFMEMORY);
3505 return FALSE;
3508 pStream->hFile = hFile;
3509 strcpy(pStream->lpszUrl, lpszUrlName);
3510 return pStream;
3513 /***********************************************************************
3514 * RetrieveUrlCacheEntryStreamW (WININET.@)
3517 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3518 IN LPCWSTR lpszUrlName,
3519 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3520 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3521 IN BOOL fRandomRead,
3522 IN DWORD dwReserved
3525 DWORD size;
3526 int url_len;
3527 /* NOTE: this is not the same as the way that the native
3528 * version allocates 'stream' handles. I did it this way
3529 * as it is much easier and no applications should depend
3530 * on this behaviour. (Native version appears to allocate
3531 * indices into a table)
3533 STREAM_HANDLE * pStream;
3534 HANDLE hFile;
3536 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3537 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3539 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3540 lpCacheEntryInfo,
3541 lpdwCacheEntryInfoBufferSize,
3542 dwReserved))
3544 return NULL;
3547 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3548 GENERIC_READ,
3549 FILE_SHARE_READ,
3550 NULL,
3551 OPEN_EXISTING,
3552 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3553 NULL);
3554 if (hFile == INVALID_HANDLE_VALUE)
3555 return FALSE;
3557 /* allocate handle storage space */
3558 size = sizeof(STREAM_HANDLE);
3559 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3560 size += url_len;
3561 pStream = heap_alloc(size);
3562 if (!pStream)
3564 CloseHandle(hFile);
3565 SetLastError(ERROR_OUTOFMEMORY);
3566 return FALSE;
3569 pStream->hFile = hFile;
3570 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3571 return pStream;
3574 /***********************************************************************
3575 * UnlockUrlCacheEntryStream (WININET.@)
3578 BOOL WINAPI UnlockUrlCacheEntryStream(
3579 IN HANDLE hUrlCacheStream,
3580 IN DWORD dwReserved
3583 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3585 if (dwReserved != 0)
3587 ERR("dwReserved != 0\n");
3588 SetLastError(ERROR_INVALID_PARAMETER);
3589 return FALSE;
3592 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3594 SetLastError(ERROR_INVALID_HANDLE);
3595 return FALSE;
3598 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3599 return FALSE;
3601 CloseHandle(pStream->hFile);
3602 heap_free(pStream);
3603 return TRUE;
3607 /***********************************************************************
3608 * DeleteUrlCacheEntryA (WININET.@)
3611 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3613 URLCACHECONTAINER * pContainer;
3614 LPURLCACHE_HEADER pHeader;
3615 struct _HASH_ENTRY * pHashEntry;
3616 DWORD error;
3617 BOOL ret;
3619 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3621 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3622 if (error != ERROR_SUCCESS)
3624 SetLastError(error);
3625 return FALSE;
3628 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3629 if (error != ERROR_SUCCESS)
3631 SetLastError(error);
3632 return FALSE;
3635 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3636 return FALSE;
3638 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3640 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3641 TRACE("entry %s not found!\n", lpszUrlName);
3642 SetLastError(ERROR_FILE_NOT_FOUND);
3643 return FALSE;
3646 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3648 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3650 return ret;
3653 /***********************************************************************
3654 * DeleteUrlCacheEntryW (WININET.@)
3657 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3659 URLCACHECONTAINER * pContainer;
3660 LPURLCACHE_HEADER pHeader;
3661 struct _HASH_ENTRY * pHashEntry;
3662 LPSTR urlA;
3663 DWORD error;
3664 BOOL ret;
3666 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3668 urlA = heap_strdupWtoA(lpszUrlName);
3669 if (!urlA)
3671 SetLastError(ERROR_OUTOFMEMORY);
3672 return FALSE;
3675 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3676 if (error != ERROR_SUCCESS)
3678 heap_free(urlA);
3679 SetLastError(error);
3680 return FALSE;
3683 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3684 if (error != ERROR_SUCCESS)
3686 heap_free(urlA);
3687 SetLastError(error);
3688 return FALSE;
3691 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3693 heap_free(urlA);
3694 return FALSE;
3697 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3699 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3700 TRACE("entry %s not found!\n", debugstr_a(urlA));
3701 heap_free(urlA);
3702 SetLastError(ERROR_FILE_NOT_FOUND);
3703 return FALSE;
3706 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3708 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3709 heap_free(urlA);
3710 return ret;
3713 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3715 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3716 return TRUE;
3719 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3721 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3722 return TRUE;
3725 /***********************************************************************
3726 * CreateCacheContainerA (WININET.@)
3728 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3729 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3731 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3732 d1, d2, d3, d4, d5, d6, d7, d8);
3733 return TRUE;
3736 /***********************************************************************
3737 * CreateCacheContainerW (WININET.@)
3739 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3740 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3742 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3743 d1, d2, d3, d4, d5, d6, d7, d8);
3744 return TRUE;
3747 /***********************************************************************
3748 * FindFirstUrlCacheContainerA (WININET.@)
3750 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3752 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3753 return NULL;
3756 /***********************************************************************
3757 * FindFirstUrlCacheContainerW (WININET.@)
3759 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3761 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3762 return NULL;
3765 /***********************************************************************
3766 * FindNextUrlCacheContainerA (WININET.@)
3768 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3770 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3771 return FALSE;
3774 /***********************************************************************
3775 * FindNextUrlCacheContainerW (WININET.@)
3777 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3779 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3780 return FALSE;
3783 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3784 LPCSTR lpszUrlSearchPattern,
3785 DWORD dwFlags,
3786 DWORD dwFilter,
3787 GROUPID GroupId,
3788 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3789 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3790 LPVOID lpReserved,
3791 LPDWORD pcbReserved2,
3792 LPVOID lpReserved3
3795 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3796 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3797 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3798 SetLastError(ERROR_FILE_NOT_FOUND);
3799 return NULL;
3802 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3803 LPCWSTR lpszUrlSearchPattern,
3804 DWORD dwFlags,
3805 DWORD dwFilter,
3806 GROUPID GroupId,
3807 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3808 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3809 LPVOID lpReserved,
3810 LPDWORD pcbReserved2,
3811 LPVOID lpReserved3
3814 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3815 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3816 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3817 SetLastError(ERROR_FILE_NOT_FOUND);
3818 return NULL;
3821 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3823 typedef struct URLCacheFindEntryHandle
3825 DWORD dwMagic;
3826 LPWSTR lpszUrlSearchPattern;
3827 DWORD dwContainerIndex;
3828 DWORD dwHashTableIndex;
3829 DWORD dwHashEntryIndex;
3830 } URLCacheFindEntryHandle;
3832 /***********************************************************************
3833 * FindFirstUrlCacheEntryA (WININET.@)
3836 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3837 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3839 URLCacheFindEntryHandle *pEntryHandle;
3841 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3843 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3844 if (!pEntryHandle)
3845 return NULL;
3847 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3848 if (lpszUrlSearchPattern)
3850 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3851 if (!pEntryHandle->lpszUrlSearchPattern)
3853 heap_free(pEntryHandle);
3854 return NULL;
3857 else
3858 pEntryHandle->lpszUrlSearchPattern = NULL;
3859 pEntryHandle->dwContainerIndex = 0;
3860 pEntryHandle->dwHashTableIndex = 0;
3861 pEntryHandle->dwHashEntryIndex = 0;
3863 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3865 heap_free(pEntryHandle);
3866 return NULL;
3868 return pEntryHandle;
3871 /***********************************************************************
3872 * FindFirstUrlCacheEntryW (WININET.@)
3875 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3876 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3878 URLCacheFindEntryHandle *pEntryHandle;
3880 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3882 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3883 if (!pEntryHandle)
3884 return NULL;
3886 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3887 if (lpszUrlSearchPattern)
3889 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3890 if (!pEntryHandle->lpszUrlSearchPattern)
3892 heap_free(pEntryHandle);
3893 return NULL;
3896 else
3897 pEntryHandle->lpszUrlSearchPattern = NULL;
3898 pEntryHandle->dwContainerIndex = 0;
3899 pEntryHandle->dwHashTableIndex = 0;
3900 pEntryHandle->dwHashEntryIndex = 0;
3902 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3904 heap_free(pEntryHandle);
3905 return NULL;
3907 return pEntryHandle;
3910 static BOOL FindNextUrlCacheEntryInternal(
3911 HANDLE hEnumHandle,
3912 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3913 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3914 BOOL unicode)
3916 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3917 URLCACHECONTAINER * pContainer;
3919 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3921 SetLastError(ERROR_INVALID_HANDLE);
3922 return FALSE;
3925 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3926 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3928 LPURLCACHE_HEADER pHeader;
3929 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3930 DWORD error;
3932 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3933 if (error != ERROR_SUCCESS)
3935 SetLastError(error);
3936 return FALSE;
3939 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3940 return FALSE;
3942 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3943 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3945 const struct _HASH_ENTRY *pHashEntry = NULL;
3946 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3947 pEntryHandle->dwHashEntryIndex++)
3949 const URL_CACHEFILE_ENTRY *pUrlEntry;
3950 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3952 if (pEntry->dwSignature != URL_SIGNATURE)
3953 continue;
3955 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3956 TRACE("Found URL: %s\n",
3957 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3958 TRACE("Header info: %s\n",
3959 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3961 error = URLCache_CopyEntry(
3962 pContainer,
3963 pHeader,
3964 lpNextCacheEntryInfo,
3965 lpdwNextCacheEntryInfoBufferSize,
3966 pUrlEntry,
3967 unicode);
3968 if (error != ERROR_SUCCESS)
3970 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3971 SetLastError(error);
3972 return FALSE;
3974 TRACE("Local File Name: %s\n",
3975 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3977 /* increment the current index so that next time the function
3978 * is called the next entry is returned */
3979 pEntryHandle->dwHashEntryIndex++;
3980 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3981 return TRUE;
3985 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3988 SetLastError(ERROR_NO_MORE_ITEMS);
3989 return FALSE;
3992 /***********************************************************************
3993 * FindNextUrlCacheEntryA (WININET.@)
3995 BOOL WINAPI FindNextUrlCacheEntryA(
3996 HANDLE hEnumHandle,
3997 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3998 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4000 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4002 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4003 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4006 /***********************************************************************
4007 * FindNextUrlCacheEntryW (WININET.@)
4009 BOOL WINAPI FindNextUrlCacheEntryW(
4010 HANDLE hEnumHandle,
4011 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4012 LPDWORD lpdwNextCacheEntryInfoBufferSize
4015 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4017 return FindNextUrlCacheEntryInternal(hEnumHandle,
4018 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4019 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4022 /***********************************************************************
4023 * FindCloseUrlCache (WININET.@)
4025 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4027 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
4029 TRACE("(%p)\n", hEnumHandle);
4031 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4033 SetLastError(ERROR_INVALID_HANDLE);
4034 return FALSE;
4037 pEntryHandle->dwMagic = 0;
4038 heap_free(pEntryHandle->lpszUrlSearchPattern);
4039 heap_free(pEntryHandle);
4040 return TRUE;
4043 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4044 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4046 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4047 dwSearchCondition, lpGroupId, lpReserved);
4048 return NULL;
4051 BOOL WINAPI FindNextUrlCacheEntryExA(
4052 HANDLE hEnumHandle,
4053 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4054 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4055 LPVOID lpReserved,
4056 LPDWORD pcbReserved2,
4057 LPVOID lpReserved3
4060 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4061 lpReserved, pcbReserved2, lpReserved3);
4062 return FALSE;
4065 BOOL WINAPI FindNextUrlCacheEntryExW(
4066 HANDLE hEnumHandle,
4067 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4068 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4069 LPVOID lpReserved,
4070 LPDWORD pcbReserved2,
4071 LPVOID lpReserved3
4074 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4075 lpReserved, pcbReserved2, lpReserved3);
4076 return FALSE;
4079 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4081 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4082 return FALSE;
4085 /***********************************************************************
4086 * CreateUrlCacheGroup (WININET.@)
4089 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4091 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4092 return FALSE;
4095 /***********************************************************************
4096 * DeleteUrlCacheGroup (WININET.@)
4099 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4101 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4102 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4103 return FALSE;
4106 /***********************************************************************
4107 * SetUrlCacheEntryGroupA (WININET.@)
4110 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4111 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4112 LPVOID lpReserved)
4114 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4115 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4116 pbGroupAttributes, cbGroupAttributes, lpReserved);
4117 SetLastError(ERROR_FILE_NOT_FOUND);
4118 return FALSE;
4121 /***********************************************************************
4122 * SetUrlCacheEntryGroupW (WININET.@)
4125 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4126 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4127 LPVOID lpReserved)
4129 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4130 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4131 pbGroupAttributes, cbGroupAttributes, lpReserved);
4132 SetLastError(ERROR_FILE_NOT_FOUND);
4133 return FALSE;
4136 /***********************************************************************
4137 * GetUrlCacheConfigInfoW (WININET.@)
4139 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4141 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4142 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4143 return FALSE;
4146 /***********************************************************************
4147 * GetUrlCacheConfigInfoA (WININET.@)
4149 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4151 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4152 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4153 return FALSE;
4156 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4157 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4158 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4160 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4161 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4162 lpdwGroupInfo, lpReserved);
4163 return FALSE;
4166 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4167 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4168 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4170 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4171 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4172 lpdwGroupInfo, lpReserved);
4173 return FALSE;
4176 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4177 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4179 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4180 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4181 return TRUE;
4184 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4185 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4187 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4188 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4189 return TRUE;
4192 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4194 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4195 return TRUE;
4198 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4200 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4201 return TRUE;
4204 /***********************************************************************
4205 * DeleteIE3Cache (WININET.@)
4207 * Deletes the files used by the IE3 URL caching system.
4209 * PARAMS
4210 * hWnd [I] A dummy window.
4211 * hInst [I] Instance of process calling the function.
4212 * lpszCmdLine [I] Options used by function.
4213 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4215 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4217 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4218 return 0;
4221 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
4222 FILETIME *pftLastModified)
4224 BOOL ret;
4225 FILETIME now, expired;
4227 *pftLastModified = pUrlEntry->LastModifiedTime;
4228 GetSystemTimeAsFileTime(&now);
4229 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
4230 pUrlEntry->wExpiredTime, &expired);
4231 /* If the expired time is 0, it's interpreted as not expired */
4232 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4233 ret = FALSE;
4234 else
4235 ret = CompareFileTime(&expired, &now) < 0;
4236 return ret;
4239 /***********************************************************************
4240 * IsUrlCacheEntryExpiredA (WININET.@)
4242 * PARAMS
4243 * url [I] Url
4244 * dwFlags [I] Unknown
4245 * pftLastModified [O] Last modified time
4247 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4249 LPURLCACHE_HEADER pHeader;
4250 struct _HASH_ENTRY * pHashEntry;
4251 const CACHEFILE_ENTRY * pEntry;
4252 const URL_CACHEFILE_ENTRY * pUrlEntry;
4253 URLCACHECONTAINER * pContainer;
4254 BOOL expired;
4256 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4258 if (!url || !pftLastModified)
4259 return TRUE;
4260 if (dwFlags)
4261 FIXME("unknown flags 0x%08x\n", dwFlags);
4263 /* Any error implies that the URL is expired, i.e. not in the cache */
4264 if (URLCacheContainers_FindContainerA(url, &pContainer))
4266 memset(pftLastModified, 0, sizeof(*pftLastModified));
4267 return TRUE;
4270 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4272 memset(pftLastModified, 0, sizeof(*pftLastModified));
4273 return TRUE;
4276 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4278 memset(pftLastModified, 0, sizeof(*pftLastModified));
4279 return TRUE;
4282 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4284 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4285 memset(pftLastModified, 0, sizeof(*pftLastModified));
4286 TRACE("entry %s not found!\n", url);
4287 return TRUE;
4290 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4291 if (pEntry->dwSignature != URL_SIGNATURE)
4293 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4294 memset(pftLastModified, 0, sizeof(*pftLastModified));
4295 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4296 return TRUE;
4299 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4300 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4302 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4304 return expired;
4307 /***********************************************************************
4308 * IsUrlCacheEntryExpiredW (WININET.@)
4310 * PARAMS
4311 * url [I] Url
4312 * dwFlags [I] Unknown
4313 * pftLastModified [O] Last modified time
4315 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4317 LPURLCACHE_HEADER pHeader;
4318 struct _HASH_ENTRY * pHashEntry;
4319 const CACHEFILE_ENTRY * pEntry;
4320 const URL_CACHEFILE_ENTRY * pUrlEntry;
4321 URLCACHECONTAINER * pContainer;
4322 BOOL expired;
4324 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4326 if (!url || !pftLastModified)
4327 return TRUE;
4328 if (dwFlags)
4329 FIXME("unknown flags 0x%08x\n", dwFlags);
4331 /* Any error implies that the URL is expired, i.e. not in the cache */
4332 if (URLCacheContainers_FindContainerW(url, &pContainer))
4334 memset(pftLastModified, 0, sizeof(*pftLastModified));
4335 return TRUE;
4338 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
4340 memset(pftLastModified, 0, sizeof(*pftLastModified));
4341 return TRUE;
4344 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4346 memset(pftLastModified, 0, sizeof(*pftLastModified));
4347 return TRUE;
4350 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4352 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4353 memset(pftLastModified, 0, sizeof(*pftLastModified));
4354 TRACE("entry %s not found!\n", debugstr_w(url));
4355 return TRUE;
4358 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4360 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4361 memset(pftLastModified, 0, sizeof(*pftLastModified));
4362 TRACE("entry %s not found!\n", debugstr_w(url));
4363 return TRUE;
4366 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4367 if (pEntry->dwSignature != URL_SIGNATURE)
4369 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4370 memset(pftLastModified, 0, sizeof(*pftLastModified));
4371 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4372 return TRUE;
4375 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4376 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4378 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4380 return expired;
4383 /***********************************************************************
4384 * GetDiskInfoA (WININET.@)
4386 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4388 BOOL ret;
4389 ULARGE_INTEGER bytes_free, bytes_total;
4391 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4393 if (!path)
4395 SetLastError(ERROR_INVALID_PARAMETER);
4396 return FALSE;
4399 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4401 if (cluster_size) *cluster_size = 1;
4402 if (free) *free = bytes_free.QuadPart;
4403 if (total) *total = bytes_total.QuadPart;
4405 return ret;
4408 /***********************************************************************
4409 * RegisterUrlCacheNotification (WININET.@)
4411 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4413 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4414 return 0;
4417 /***********************************************************************
4418 * IncrementUrlCacheHeaderData (WININET.@)
4420 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4422 FIXME("(%u, %p)\n", index, data);
4423 return FALSE;
4426 /***********************************************************************
4427 * RunOnceUrlCache (WININET.@)
4430 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4432 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4433 return 0;
4436 BOOL init_urlcache(void)
4438 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4439 if(!dll_unload_event)
4440 return FALSE;
4442 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4443 if(!free_cache_running) {
4444 CloseHandle(dll_unload_event);
4445 return FALSE;
4448 URLCacheContainers_CreateDefaults();
4449 return TRUE;
4452 void free_urlcache(void)
4454 SetEvent(dll_unload_event);
4455 WaitForSingleObject(free_cache_running, INFINITE);
4456 ReleaseSemaphore(free_cache_running, 1, NULL);
4457 CloseHandle(free_cache_running);
4458 CloseHandle(dll_unload_event);
4460 URLCacheContainers_DeleteAll();