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