wininet: Partially implement FreeUrlCacheSpaceW.
[wine.git] / dlls / wininet / urlcache.c
blob0c883726baeda4ae76bd73eeb638b615c81161d3
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 BLOCKSIZE 128
66 #define HASHTABLE_SIZE 448
67 #define HASHTABLE_BLOCKSIZE 7
68 #define HASHTABLE_FREE 3
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
71 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
72 #define NEWFILE_NUM_BLOCKS 0xd80
73 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
75 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
76 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
77 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
78 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
79 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
81 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
83 typedef struct _CACHEFILE_ENTRY
85 /* union
86 {*/
87 DWORD dwSignature; /* e.g. "URL " */
88 /* CHAR szSignature[4];
89 };*/
90 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
91 } CACHEFILE_ENTRY;
93 typedef struct _URL_CACHEFILE_ENTRY
95 CACHEFILE_ENTRY CacheFileEntry;
96 FILETIME LastModifiedTime;
97 FILETIME LastAccessTime;
98 WORD wExpiredDate; /* expire date in dos format */
99 WORD wExpiredTime; /* expire time in dos format */
100 DWORD dwUnknown1; /* usually zero */
101 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
102 DWORD dwUnknown2; /* usually zero */
103 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
104 DWORD dwUnknown3; /* usually 0x60 */
105 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
106 BYTE CacheDir; /* index of cache directory this url is stored in */
107 BYTE Unknown4; /* usually zero */
108 WORD wUnknown5; /* usually 0x1010 */
109 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
110 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
111 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
112 DWORD dwHeaderInfoSize;
113 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
114 WORD wLastSyncDate; /* last sync date in dos format */
115 WORD wLastSyncTime; /* last sync time in dos format */
116 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
117 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
118 WORD wUnknownDate; /* usually same as wLastSyncDate */
119 WORD wUnknownTime; /* usually same as wLastSyncTime */
120 DWORD dwUnknown7; /* usually zero */
121 DWORD dwUnknown8; /* usually zero */
122 /* packing to dword align start of next field */
123 /* CHAR szSourceUrlName[]; (url) */
124 /* packing to dword align start of next field */
125 /* CHAR szLocalFileName[]; (local file name excluding path) */
126 /* packing to dword align start of next field */
127 /* CHAR szHeaderInfo[]; (header info) */
128 } URL_CACHEFILE_ENTRY;
130 struct _HASH_ENTRY
132 DWORD dwHashKey;
133 DWORD dwOffsetEntry;
136 typedef struct _HASH_CACHEFILE_ENTRY
138 CACHEFILE_ENTRY CacheFileEntry;
139 DWORD dwAddressNext;
140 DWORD dwHashTableNumber;
141 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
142 } HASH_CACHEFILE_ENTRY;
144 typedef struct _DIRECTORY_DATA
146 DWORD dwNumFiles;
147 char filename[DIR_LENGTH];
148 } DIRECTORY_DATA;
150 typedef struct _URLCACHE_HEADER
152 char szSignature[28];
153 DWORD dwFileSize;
154 DWORD dwOffsetFirstHashTable;
155 DWORD dwIndexCapacityInBlocks;
156 DWORD dwBlocksInUse;
157 DWORD dwUnknown1;
158 ULARGE_INTEGER CacheLimit;
159 ULARGE_INTEGER CacheUsage;
160 ULARGE_INTEGER ExemptUsage;
161 DWORD DirectoryCount; /* number of directory_data's */
162 DIRECTORY_DATA directory_data[1]; /* first directory entry */
163 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
164 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
166 typedef struct _STREAM_HANDLE
168 HANDLE hFile;
169 CHAR lpszUrl[1];
170 } STREAM_HANDLE;
172 typedef struct _URLCACHECONTAINER
174 struct list entry; /* part of a list */
175 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
176 LPWSTR path; /* path to url container directory */
177 HANDLE hMapping; /* handle of file mapping */
178 DWORD file_size; /* size of file when mapping was opened */
179 HANDLE hMutex; /* handle of mutex */
180 } URLCACHECONTAINER;
183 /* List of all containers available */
184 static struct list UrlContainers = LIST_INIT(UrlContainers);
186 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
188 /***********************************************************************
189 * URLCache_PathToObjectName (Internal)
191 * Converts a path to a name suitable for use as a Win32 object name.
192 * Replaces '\\' characters in-place with the specified character
193 * (usually '_' or '!')
195 * RETURNS
196 * nothing
199 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
201 for (; *lpszPath; lpszPath++)
203 if (*lpszPath == '\\')
204 *lpszPath = replace;
208 /***********************************************************************
209 * URLCacheContainer_OpenIndex (Internal)
211 * Opens the index file and saves mapping handle in hCacheIndexMapping
213 * RETURNS
214 * ERROR_SUCCESS if succeeded
215 * Any other Win32 error code if failed
218 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
220 HANDLE hFile;
221 WCHAR wszFilePath[MAX_PATH];
222 DWORD dwFileSize;
224 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
225 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
227 if (pContainer->hMapping)
228 return ERROR_SUCCESS;
230 strcpyW(wszFilePath, pContainer->path);
231 strcatW(wszFilePath, wszIndex);
233 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
234 if (hFile == INVALID_HANDLE_VALUE)
236 /* Maybe the directory wasn't there? Try to create it */
237 if (CreateDirectoryW(pContainer->path, 0))
238 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
240 if (hFile == INVALID_HANDLE_VALUE)
242 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
243 return GetLastError();
246 /* At this stage we need the mutex because we may be about to create the
247 * file.
249 WaitForSingleObject(pContainer->hMutex, INFINITE);
251 dwFileSize = GetFileSize(hFile, NULL);
252 if (dwFileSize == INVALID_FILE_SIZE)
254 ReleaseMutex(pContainer->hMutex);
255 return GetLastError();
258 if (dwFileSize == 0)
260 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
261 HKEY key;
262 char achZeroes[0x1000];
263 DWORD dwOffset;
264 DWORD dwError = ERROR_SUCCESS;
266 /* Write zeroes to the entire file so we can safely map it without
267 * fear of getting a SEGV because the disk is full.
269 memset(achZeroes, 0, sizeof(achZeroes));
270 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
272 DWORD dwWrite = sizeof(achZeroes);
273 DWORD dwWritten;
275 if (NEWFILE_SIZE - dwOffset < dwWrite)
276 dwWrite = NEWFILE_SIZE - dwOffset;
277 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
278 dwWritten != dwWrite)
280 /* If we fail to write, we need to return the error that
281 * cause the problem and also make sure the file is no
282 * longer there, if possible.
284 dwError = GetLastError();
286 break;
290 if (dwError == ERROR_SUCCESS)
292 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
294 if (hMapping)
296 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
298 if (pHeader)
300 WCHAR *pwchDir;
301 WCHAR wszDirPath[MAX_PATH];
302 FILETIME ft;
303 int i, j;
304 HASH_CACHEFILE_ENTRY *pHashEntry;
306 dwFileSize = NEWFILE_SIZE;
308 /* First set some constants and defaults in the header */
309 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
310 pHeader->dwFileSize = dwFileSize;
311 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
312 /* 127MB - taken from default for Windows 2000 */
313 pHeader->CacheLimit.QuadPart = 0x07ff5400;
314 /* Copied from a Windows 2000 cache index */
315 pHeader->DirectoryCount = 4;
317 /* If the registry has a cache size set, use the registry value */
318 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
320 DWORD dw;
321 DWORD len = sizeof(dw);
322 DWORD keytype;
324 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
325 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
326 keytype == REG_DWORD)
328 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
330 RegCloseKey(key);
333 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
335 /* Last step - create the directories */
337 strcpyW(wszDirPath, pContainer->path);
338 pwchDir = wszDirPath + strlenW(wszDirPath);
339 pwchDir[8] = 0;
341 GetSystemTimeAsFileTime(&ft);
343 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
345 pHeader->directory_data[i].dwNumFiles = 0;
346 for (j = 0;; ++j)
348 int k;
349 ULONGLONG n = ft.dwHighDateTime;
351 /* Generate a file name to attempt to create.
352 * This algorithm will create what will appear
353 * to be random and unrelated directory names
354 * of up to 9 characters in length.
356 n <<= 32;
357 n += ft.dwLowDateTime;
358 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
360 for (k = 0; k < 8; ++k)
362 int r = (n % 36);
364 /* Dividing by a prime greater than 36 helps
365 * with the appearance of randomness
367 n /= 37;
369 if (r < 10)
370 pwchDir[k] = '0' + r;
371 else
372 pwchDir[k] = 'A' + (r - 10);
375 if (CreateDirectoryW(wszDirPath, 0))
377 /* The following is OK because we generated an
378 * 8 character directory name made from characters
379 * [A-Z0-9], which are equivalent for all code
380 * pages and for UTF-16
382 for (k = 0; k < 8; ++k)
383 pHeader->directory_data[i].filename[k] = pwchDir[k];
384 break;
386 else if (j >= 255)
388 /* Give up. The most likely cause of this
389 * is a full disk, but whatever the cause
390 * is, it should be more than apparent that
391 * we won't succeed.
393 dwError = GetLastError();
394 break;
399 UnmapViewOfFile(pHeader);
401 else
403 dwError = GetLastError();
405 CloseHandle(hMapping);
407 else
409 dwError = GetLastError();
413 if (dwError)
415 CloseHandle(hFile);
416 DeleteFileW(wszFilePath);
417 ReleaseMutex(pContainer->hMutex);
418 return dwError;
423 ReleaseMutex(pContainer->hMutex);
425 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
426 URLCache_PathToObjectName(wszFilePath, '_');
427 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
428 if (!pContainer->hMapping)
429 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
430 CloseHandle(hFile);
431 if (!pContainer->hMapping)
433 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
434 return GetLastError();
437 return ERROR_SUCCESS;
440 /***********************************************************************
441 * URLCacheContainer_CloseIndex (Internal)
443 * Closes the index
445 * RETURNS
446 * nothing
449 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
451 CloseHandle(pContainer->hMapping);
452 pContainer->hMapping = NULL;
455 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
457 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
458 int cache_prefix_len = strlenW(cache_prefix);
460 if (!pContainer)
462 return FALSE;
465 pContainer->hMapping = NULL;
466 pContainer->file_size = 0;
468 pContainer->path = heap_strdupW(path);
469 if (!pContainer->path)
471 HeapFree(GetProcessHeap(), 0, pContainer);
472 return FALSE;
475 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
476 if (!pContainer->cache_prefix)
478 HeapFree(GetProcessHeap(), 0, pContainer->path);
479 HeapFree(GetProcessHeap(), 0, pContainer);
480 return FALSE;
483 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
485 CharLowerW(mutex_name);
486 URLCache_PathToObjectName(mutex_name, '!');
488 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
490 ERR("couldn't create mutex (error is %d)\n", GetLastError());
491 HeapFree(GetProcessHeap(), 0, pContainer->path);
492 HeapFree(GetProcessHeap(), 0, pContainer);
493 return FALSE;
496 list_add_head(&UrlContainers, &pContainer->entry);
498 return TRUE;
501 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
503 list_remove(&pContainer->entry);
505 URLCacheContainer_CloseIndex(pContainer);
506 CloseHandle(pContainer->hMutex);
507 HeapFree(GetProcessHeap(), 0, pContainer->path);
508 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
509 HeapFree(GetProcessHeap(), 0, pContainer);
512 void URLCacheContainers_CreateDefaults(void)
514 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
515 static const WCHAR UrlPrefix[] = {0};
516 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
517 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
518 static const WCHAR CookieSuffix[] = {0};
519 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
520 static const struct
522 int nFolder; /* CSIDL_* constant */
523 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
524 const WCHAR * cache_prefix; /* prefix used to reference the container */
525 } DefaultContainerData[] =
527 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
528 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
529 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
531 DWORD i;
533 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
535 WCHAR wszCachePath[MAX_PATH];
536 WCHAR wszMutexName[MAX_PATH];
537 int path_len, suffix_len;
539 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
541 ERR("Couldn't get path for default container %u\n", i);
542 continue;
544 path_len = strlenW(wszCachePath);
545 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
547 if (path_len + suffix_len + 2 > MAX_PATH)
549 ERR("Path too long\n");
550 continue;
553 wszCachePath[path_len] = '\\';
554 wszCachePath[path_len+1] = 0;
556 strcpyW(wszMutexName, wszCachePath);
558 if (suffix_len)
560 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
561 wszCachePath[path_len + suffix_len + 1] = '\\';
562 wszCachePath[path_len + suffix_len + 2] = '\0';
565 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
569 void URLCacheContainers_DeleteAll(void)
571 while(!list_empty(&UrlContainers))
572 URLCacheContainer_DeleteContainer(
573 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
577 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
579 URLCACHECONTAINER * pContainer;
581 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
583 if(!lpwszUrl)
584 return ERROR_INVALID_PARAMETER;
586 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
588 int prefix_len = strlenW(pContainer->cache_prefix);
589 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
591 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
592 *ppContainer = pContainer;
593 return ERROR_SUCCESS;
596 ERR("no container found\n");
597 return ERROR_FILE_NOT_FOUND;
600 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
602 LPWSTR url = NULL;
603 DWORD ret;
605 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
606 return ERROR_OUTOFMEMORY;
608 ret = URLCacheContainers_FindContainerW(url, ppContainer);
609 HeapFree(GetProcessHeap(), 0, url);
610 return ret;
613 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
615 DWORD i = 0;
616 URLCACHECONTAINER * pContainer;
618 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
620 /* non-NULL search pattern only returns one container ever */
621 if (lpwszSearchPattern && dwIndex > 0)
622 return FALSE;
624 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
626 if (lpwszSearchPattern)
628 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
630 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
631 *ppContainer = pContainer;
632 return TRUE;
635 else
637 if (i == dwIndex)
639 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
640 *ppContainer = pContainer;
641 return TRUE;
644 i++;
646 return FALSE;
649 /***********************************************************************
650 * URLCacheContainer_LockIndex (Internal)
652 * Locks the index for system-wide exclusive access.
654 * RETURNS
655 * Cache file header if successful
656 * NULL if failed and calls SetLastError.
658 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
660 BYTE index;
661 LPVOID pIndexData;
662 URLCACHE_HEADER * pHeader;
663 DWORD error;
665 /* acquire mutex */
666 WaitForSingleObject(pContainer->hMutex, INFINITE);
668 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
670 if (!pIndexData)
672 ReleaseMutex(pContainer->hMutex);
673 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
674 return NULL;
676 pHeader = (URLCACHE_HEADER *)pIndexData;
678 /* file has grown - we need to remap to prevent us getting
679 * access violations when we try and access beyond the end
680 * of the memory mapped file */
681 if (pHeader->dwFileSize != pContainer->file_size)
683 UnmapViewOfFile( pHeader );
684 URLCacheContainer_CloseIndex(pContainer);
685 error = URLCacheContainer_OpenIndex(pContainer);
686 if (error != ERROR_SUCCESS)
688 ReleaseMutex(pContainer->hMutex);
689 SetLastError(error);
690 return NULL;
692 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
694 if (!pIndexData)
696 ReleaseMutex(pContainer->hMutex);
697 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
698 return NULL;
700 pHeader = (URLCACHE_HEADER *)pIndexData;
703 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
705 for (index = 0; index < pHeader->DirectoryCount; index++)
707 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
710 return pHeader;
713 /***********************************************************************
714 * URLCacheContainer_UnlockIndex (Internal)
717 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
719 /* release mutex */
720 ReleaseMutex(pContainer->hMutex);
721 return UnmapViewOfFile(pHeader);
725 #ifndef CHAR_BIT
726 #define CHAR_BIT (8 * sizeof(CHAR))
727 #endif
729 /***********************************************************************
730 * URLCache_Allocation_BlockIsFree (Internal)
732 * Is the specified block number free?
734 * RETURNS
735 * zero if free
736 * non-zero otherwise
739 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
741 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
742 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
745 /***********************************************************************
746 * URLCache_Allocation_BlockFree (Internal)
748 * Marks the specified block as free
750 * RETURNS
751 * nothing
754 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
756 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
757 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
760 /***********************************************************************
761 * URLCache_Allocation_BlockAllocate (Internal)
763 * Marks the specified block as allocated
765 * RETURNS
766 * nothing
769 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
771 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
772 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
775 /***********************************************************************
776 * URLCache_FindFirstFreeEntry (Internal)
778 * Finds and allocates the first block of free space big enough and
779 * sets ppEntry to point to it.
781 * RETURNS
782 * TRUE if it had enough space
783 * FALSE if it couldn't find enough space
786 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
788 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
789 DWORD dwBlockNumber;
790 DWORD dwFreeCounter;
791 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
793 for (dwFreeCounter = 0;
794 dwFreeCounter < dwBlocksNeeded &&
795 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
796 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
797 dwFreeCounter++)
798 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
800 if (dwFreeCounter == dwBlocksNeeded)
802 DWORD index;
803 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
804 for (index = 0; index < dwBlocksNeeded; index++)
805 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
806 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
807 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
808 return TRUE;
811 FIXME("Grow file\n");
812 return FALSE;
815 /***********************************************************************
816 * URLCache_DeleteEntry (Internal)
818 * Deletes the specified entry and frees the space allocated to it
820 * RETURNS
821 * TRUE if it succeeded
822 * FALSE if it failed
825 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
827 DWORD dwStartBlock;
828 DWORD dwBlock;
829 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
831 /* update allocation table */
832 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
833 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
834 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
836 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
837 return TRUE;
840 /***********************************************************************
841 * URLCache_LocalFileNameToPathW (Internal)
843 * Copies the full path to the specified buffer given the local file
844 * name and the index of the directory it is in. Always sets value in
845 * lpBufferSize to the required buffer size (in bytes).
847 * RETURNS
848 * TRUE if the buffer was big enough
849 * FALSE if the buffer was too small
852 static BOOL URLCache_LocalFileNameToPathW(
853 const URLCACHECONTAINER * pContainer,
854 LPCURLCACHE_HEADER pHeader,
855 LPCSTR szLocalFileName,
856 BYTE Directory,
857 LPWSTR wszPath,
858 LPLONG lpBufferSize)
860 LONG nRequired;
861 int path_len = strlenW(pContainer->path);
862 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
863 if (Directory >= pHeader->DirectoryCount)
865 *lpBufferSize = 0;
866 return FALSE;
869 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
870 if (nRequired <= *lpBufferSize)
872 int dir_len;
874 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
875 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
876 wszPath[dir_len + path_len] = '\\';
877 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
878 *lpBufferSize = nRequired;
879 return TRUE;
881 *lpBufferSize = nRequired;
882 return FALSE;
885 /***********************************************************************
886 * URLCache_LocalFileNameToPathA (Internal)
888 * Copies the full path to the specified buffer given the local file
889 * name and the index of the directory it is in. Always sets value in
890 * lpBufferSize to the required buffer size.
892 * RETURNS
893 * TRUE if the buffer was big enough
894 * FALSE if the buffer was too small
897 static BOOL URLCache_LocalFileNameToPathA(
898 const URLCACHECONTAINER * pContainer,
899 LPCURLCACHE_HEADER pHeader,
900 LPCSTR szLocalFileName,
901 BYTE Directory,
902 LPSTR szPath,
903 LPLONG lpBufferSize)
905 LONG nRequired;
906 int path_len, file_name_len, dir_len;
908 if (Directory >= pHeader->DirectoryCount)
910 *lpBufferSize = 0;
911 return FALSE;
914 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
915 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
916 dir_len = DIR_LENGTH;
918 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
919 if (nRequired < *lpBufferSize)
921 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
922 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
923 szPath[path_len + dir_len] = '\\';
924 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
925 *lpBufferSize = nRequired;
926 return TRUE;
928 *lpBufferSize = nRequired;
929 return FALSE;
932 /* Just like DosDateTimeToFileTime, except that it also maps the special
933 * case of a DOS date/time of (0,0) to a filetime of (0,0).
935 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
936 FILETIME *ft)
938 if (!fatdate && !fattime)
939 ft->dwLowDateTime = ft->dwHighDateTime = 0;
940 else
941 DosDateTimeToFileTime(fatdate, fattime, ft);
944 /***********************************************************************
945 * URLCache_CopyEntry (Internal)
947 * Copies an entry from the cache index file to the Win32 structure
949 * RETURNS
950 * ERROR_SUCCESS if the buffer was big enough
951 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
954 static DWORD URLCache_CopyEntry(
955 URLCACHECONTAINER * pContainer,
956 LPCURLCACHE_HEADER pHeader,
957 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
958 LPDWORD lpdwBufferSize,
959 const URL_CACHEFILE_ENTRY * pUrlEntry,
960 BOOL bUnicode)
962 int lenUrl;
963 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
965 if (*lpdwBufferSize >= dwRequiredSize)
967 lpCacheEntryInfo->lpHeaderInfo = NULL;
968 lpCacheEntryInfo->lpszFileExtension = NULL;
969 lpCacheEntryInfo->lpszLocalFileName = NULL;
970 lpCacheEntryInfo->lpszSourceUrlName = NULL;
971 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
972 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
973 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
974 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
975 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
976 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
977 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
978 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
979 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
980 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
981 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
982 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
983 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
984 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
987 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
988 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
989 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
990 if (bUnicode)
991 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
992 else
993 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
994 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
996 /* FIXME: is source url optional? */
997 if (*lpdwBufferSize >= dwRequiredSize)
999 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1001 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1002 if (bUnicode)
1003 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1004 else
1005 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1008 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1009 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1010 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1012 if (pUrlEntry->dwOffsetLocalName)
1014 LONG nLocalFilePathSize;
1015 LPSTR lpszLocalFileName;
1016 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1017 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1018 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1019 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1021 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1023 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1025 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1026 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1027 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1029 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1031 if (*lpdwBufferSize >= dwRequiredSize)
1033 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1034 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1035 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1037 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1038 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1039 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1041 if (pUrlEntry->dwOffsetFileExtension)
1043 int lenExtension;
1045 if (bUnicode)
1046 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1047 else
1048 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1049 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1051 if (*lpdwBufferSize >= dwRequiredSize)
1053 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1054 if (bUnicode)
1055 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1056 else
1057 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1060 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1061 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1062 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1065 if (dwRequiredSize > *lpdwBufferSize)
1067 *lpdwBufferSize = dwRequiredSize;
1068 return ERROR_INSUFFICIENT_BUFFER;
1070 *lpdwBufferSize = dwRequiredSize;
1071 return ERROR_SUCCESS;
1074 /* Just like FileTimeToDosDateTime, except that it also maps the special
1075 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1077 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1078 WORD *fattime)
1080 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1081 *fatdate = *fattime = 0;
1082 else
1083 FileTimeToDosDateTime(ft, fatdate, fattime);
1086 /***********************************************************************
1087 * URLCache_SetEntryInfo (Internal)
1089 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1090 * according to the flags set by dwFieldControl.
1092 * RETURNS
1093 * ERROR_SUCCESS if the buffer was big enough
1094 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1097 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1099 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1100 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1101 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1102 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1103 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1104 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1105 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1106 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1107 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1108 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1109 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1110 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1111 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1112 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1113 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1114 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1116 return ERROR_SUCCESS;
1119 /***********************************************************************
1120 * URLCache_HashKey (Internal)
1122 * Returns the hash key for a given string
1124 * RETURNS
1125 * hash key for the string
1128 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1130 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1131 * but the algorithm and result are not the same!
1133 static const unsigned char lookupTable[256] =
1135 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1136 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1137 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1138 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1139 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1140 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1141 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1142 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1143 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1144 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1145 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1146 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1147 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1148 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1149 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1150 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1151 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1152 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1153 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1154 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1155 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1156 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1157 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1158 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1159 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1160 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1161 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1162 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1163 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1164 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1165 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1166 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1168 BYTE key[4];
1169 DWORD i;
1171 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1172 key[i] = lookupTable[i];
1174 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1176 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1177 key[i] = lookupTable[*lpszKey ^ key[i]];
1180 return *(DWORD *)key;
1183 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1185 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1188 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1190 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1191 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1192 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1195 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1197 /* structure of hash table:
1198 * 448 entries divided into 64 blocks
1199 * each block therefore contains a chain of 7 key/offset pairs
1200 * how position in table is calculated:
1201 * 1. the url is hashed in helper function
1202 * 2. the key % 64 * 8 is the offset
1203 * 3. the key in the hash table is the hash key aligned to 64
1205 * note:
1206 * there can be multiple hash tables in the file and the offset to
1207 * the next one is stored in the header of the hash table
1209 DWORD key = URLCache_HashKey(lpszUrl);
1210 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1211 HASH_CACHEFILE_ENTRY * pHashEntry;
1212 DWORD dwHashTableNumber = 0;
1214 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1216 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1217 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1218 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1220 int i;
1221 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1223 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1224 continue;
1226 /* make sure that it is in fact a hash entry */
1227 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1229 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1230 continue;
1233 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1235 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1236 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1238 /* FIXME: we should make sure that this is the right element
1239 * before returning and claiming that it is. We can do this
1240 * by doing a simple compare between the URL we were given
1241 * and the URL stored in the entry. However, this assumes
1242 * we know the format of all the entries stored in the
1243 * hash table */
1244 *ppHashEntry = pHashElement;
1245 return TRUE;
1249 return FALSE;
1252 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1254 LPSTR urlA;
1255 BOOL ret;
1257 urlA = heap_strdupWtoA(lpszUrl);
1258 if (!urlA)
1260 SetLastError(ERROR_OUTOFMEMORY);
1261 return FALSE;
1264 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1265 HeapFree(GetProcessHeap(), 0, urlA);
1266 return ret;
1269 /***********************************************************************
1270 * URLCache_HashEntrySetUse (Internal)
1272 * Searches all the hash tables in the index for the given URL and
1273 * sets the use count (stored or'ed with key)
1275 * RETURNS
1276 * TRUE if the entry was found
1277 * FALSE if the entry could not be found
1280 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1282 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1283 return TRUE;
1286 /***********************************************************************
1287 * URLCache_DeleteEntryFromHash (Internal)
1289 * Searches all the hash tables in the index for the given URL and
1290 * then if found deletes the entry.
1292 * RETURNS
1293 * TRUE if the entry was found
1294 * FALSE if the entry could not be found
1297 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1299 pHashEntry->dwHashKey = HASHTABLE_FREE;
1300 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1301 return TRUE;
1304 /***********************************************************************
1305 * URLCache_AddEntryToHash (Internal)
1307 * Searches all the hash tables for a free slot based on the offset
1308 * generated from the hash key. If a free slot is found, the offset and
1309 * key are entered into the hash table.
1311 * RETURNS
1312 * ERROR_SUCCESS if the entry was added
1313 * Any other Win32 error code if the entry could not be added
1316 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1318 /* see URLCache_FindEntryInHash for structure of hash tables */
1320 DWORD key = URLCache_HashKey(lpszUrl);
1321 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1322 HASH_CACHEFILE_ENTRY * pHashEntry;
1323 DWORD dwHashTableNumber = 0;
1324 DWORD error;
1326 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1328 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1329 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1330 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1332 int i;
1333 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1335 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1336 break;
1338 /* make sure that it is in fact a hash entry */
1339 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1341 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1342 break;
1345 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1347 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1348 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1350 pHashElement->dwHashKey = key;
1351 pHashElement->dwOffsetEntry = dwOffsetEntry;
1352 return ERROR_SUCCESS;
1356 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1357 if (error != ERROR_SUCCESS)
1358 return error;
1360 pHashEntry->HashTable[offset].dwHashKey = key;
1361 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1362 return ERROR_SUCCESS;
1365 /***********************************************************************
1366 * URLCache_CreateHashTable (Internal)
1368 * Creates a new hash table in free space and adds it to the chain of existing
1369 * hash tables.
1371 * RETURNS
1372 * ERROR_SUCCESS if the hash table was created
1373 * ERROR_DISK_FULL if the hash table could not be created
1376 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1378 DWORD dwOffset;
1379 int i;
1381 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1383 FIXME("no free space for hash table\n");
1384 return ERROR_DISK_FULL;
1387 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1389 if (pPrevHash)
1390 pPrevHash->dwAddressNext = dwOffset;
1391 else
1392 pHeader->dwOffsetFirstHashTable = dwOffset;
1393 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1394 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1395 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1396 for (i = 0; i < HASHTABLE_SIZE; i++)
1398 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1399 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1401 return ERROR_SUCCESS;
1404 /***********************************************************************
1405 * URLCache_EnumHashTables (Internal)
1407 * Enumerates the hash tables in a container.
1409 * RETURNS
1410 * TRUE if an entry was found
1411 * FALSE if there are no more tables to enumerate.
1414 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1416 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1417 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1418 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1420 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1421 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1422 continue;
1423 /* make sure that it is in fact a hash entry */
1424 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1426 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1427 (*pdwHashTableNumber)++;
1428 continue;
1431 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1432 return TRUE;
1434 return FALSE;
1437 /***********************************************************************
1438 * URLCache_EnumHashTableEntries (Internal)
1440 * Enumerates entries in a hash table and returns the next non-free entry.
1442 * RETURNS
1443 * TRUE if an entry was found
1444 * FALSE if the hash table is empty or there are no more entries to
1445 * enumerate.
1448 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1449 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1451 for (; *index < HASHTABLE_SIZE ; (*index)++)
1453 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1454 continue;
1456 *ppHashEntry = &pHashEntry->HashTable[*index];
1457 TRACE("entry found %d\n", *index);
1458 return TRUE;
1460 TRACE("no more entries (%d)\n", *index);
1461 return FALSE;
1464 /***********************************************************************
1465 * URLCache_DeleteCacheDirectory (Internal)
1467 * Erase a directory containing an URL cache.
1469 * RETURNS
1470 * TRUE success, FALSE failure/aborted.
1473 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1475 DWORD path_len;
1476 WCHAR path[MAX_PATH + 1];
1477 SHFILEOPSTRUCTW shfos;
1478 int ret;
1480 path_len = strlenW(lpszPath);
1481 if (path_len >= MAX_PATH)
1482 return FALSE;
1483 strcpyW(path, lpszPath);
1484 path[path_len + 1] = 0; /* double-NUL-terminate path */
1486 shfos.hwnd = NULL;
1487 shfos.wFunc = FO_DELETE;
1488 shfos.pFrom = path;
1489 shfos.pTo = NULL;
1490 shfos.fFlags = 0;
1491 shfos.fAnyOperationsAborted = FALSE;
1492 ret = SHFileOperationW(&shfos);
1493 if (ret)
1494 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1495 return !(ret || shfos.fAnyOperationsAborted);
1498 /***********************************************************************
1499 * FreeUrlCacheSpaceW (WININET.@)
1501 * Frees up some cache.
1503 * PARAMETERS
1504 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1505 * dwSize [I] How much space to free up.
1506 * dwSizeType [I] How to interpret dwSize.
1508 * RETURNS
1509 * TRUE success. FALSE failure.
1511 * IMPLEMENTATION
1512 * This implementation just retrieves the path of the cache directory, and
1513 * deletes its contents from the filesystem. The correct approach would
1514 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1516 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1518 URLCACHECONTAINER * pContainer;
1520 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1522 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1523 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1524 return FALSE;
1527 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1529 /* The URL cache has prefix L"" (unlike Cookies and History) */
1530 if (pContainer->cache_prefix[0] == 0)
1532 BOOL ret_del;
1533 DWORD ret_open;
1534 WaitForSingleObject(pContainer->hMutex, INFINITE);
1536 /* unlock, delete, recreate and lock cache */
1537 URLCacheContainer_CloseIndex(pContainer);
1538 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1539 ret_open = URLCacheContainer_OpenIndex(pContainer);
1541 ReleaseMutex(pContainer->hMutex);
1542 return ret_del && (ret_open == ERROR_SUCCESS);
1545 return FALSE;
1548 /***********************************************************************
1549 * FreeUrlCacheSpaceA (WININET.@)
1551 * See FreeUrlCacheSpaceW.
1553 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1555 BOOL ret = FALSE;
1556 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1557 if (lpszCachePath == NULL || path != NULL)
1558 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1559 heap_free(path);
1560 return ret;
1563 /***********************************************************************
1564 * GetUrlCacheEntryInfoExA (WININET.@)
1567 BOOL WINAPI GetUrlCacheEntryInfoExA(
1568 LPCSTR lpszUrl,
1569 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1570 LPDWORD lpdwCacheEntryInfoBufSize,
1571 LPSTR lpszReserved,
1572 LPDWORD lpdwReserved,
1573 LPVOID lpReserved,
1574 DWORD dwFlags)
1576 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1577 debugstr_a(lpszUrl),
1578 lpCacheEntryInfo,
1579 lpdwCacheEntryInfoBufSize,
1580 lpszReserved,
1581 lpdwReserved,
1582 lpReserved,
1583 dwFlags);
1585 if ((lpszReserved != NULL) ||
1586 (lpdwReserved != NULL) ||
1587 (lpReserved != NULL))
1589 ERR("Reserved value was not 0\n");
1590 SetLastError(ERROR_INVALID_PARAMETER);
1591 return FALSE;
1593 if (dwFlags != 0)
1595 FIXME("Undocumented flag(s): %x\n", dwFlags);
1596 SetLastError(ERROR_FILE_NOT_FOUND);
1597 return FALSE;
1599 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1602 /***********************************************************************
1603 * GetUrlCacheEntryInfoA (WININET.@)
1606 BOOL WINAPI GetUrlCacheEntryInfoA(
1607 IN LPCSTR lpszUrlName,
1608 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1609 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1612 LPURLCACHE_HEADER pHeader;
1613 struct _HASH_ENTRY * pHashEntry;
1614 const CACHEFILE_ENTRY * pEntry;
1615 const URL_CACHEFILE_ENTRY * pUrlEntry;
1616 URLCACHECONTAINER * pContainer;
1617 DWORD error;
1619 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1621 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1622 if (error != ERROR_SUCCESS)
1624 SetLastError(error);
1625 return FALSE;
1628 error = URLCacheContainer_OpenIndex(pContainer);
1629 if (error != ERROR_SUCCESS)
1631 SetLastError(error);
1632 return FALSE;
1635 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1636 return FALSE;
1638 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1640 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1641 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1642 SetLastError(ERROR_FILE_NOT_FOUND);
1643 return FALSE;
1646 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1647 if (pEntry->dwSignature != URL_SIGNATURE)
1649 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1650 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1651 SetLastError(ERROR_FILE_NOT_FOUND);
1652 return FALSE;
1655 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1656 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1657 if (pUrlEntry->dwOffsetHeaderInfo)
1658 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1660 if (lpdwCacheEntryInfoBufferSize)
1662 if (!lpCacheEntryInfo)
1663 *lpdwCacheEntryInfoBufferSize = 0;
1665 error = URLCache_CopyEntry(
1666 pContainer,
1667 pHeader,
1668 lpCacheEntryInfo,
1669 lpdwCacheEntryInfoBufferSize,
1670 pUrlEntry,
1671 FALSE /* ANSI */);
1672 if (error != ERROR_SUCCESS)
1674 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1675 SetLastError(error);
1676 return FALSE;
1678 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1681 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1683 return TRUE;
1686 /***********************************************************************
1687 * GetUrlCacheEntryInfoW (WININET.@)
1690 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1691 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1692 LPDWORD lpdwCacheEntryInfoBufferSize)
1694 LPURLCACHE_HEADER pHeader;
1695 struct _HASH_ENTRY * pHashEntry;
1696 const CACHEFILE_ENTRY * pEntry;
1697 const URL_CACHEFILE_ENTRY * pUrlEntry;
1698 URLCACHECONTAINER * pContainer;
1699 DWORD error;
1701 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1703 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1704 if (error != ERROR_SUCCESS)
1706 SetLastError(error);
1707 return FALSE;
1710 error = URLCacheContainer_OpenIndex(pContainer);
1711 if (error != ERROR_SUCCESS)
1713 SetLastError(error);
1714 return FALSE;
1717 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1718 return FALSE;
1720 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1722 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1723 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1724 SetLastError(ERROR_FILE_NOT_FOUND);
1725 return FALSE;
1728 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1729 if (pEntry->dwSignature != URL_SIGNATURE)
1731 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1732 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1733 SetLastError(ERROR_FILE_NOT_FOUND);
1734 return FALSE;
1737 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1738 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1739 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1741 if (lpdwCacheEntryInfoBufferSize)
1743 if (!lpCacheEntryInfo)
1744 *lpdwCacheEntryInfoBufferSize = 0;
1746 error = URLCache_CopyEntry(
1747 pContainer,
1748 pHeader,
1749 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1750 lpdwCacheEntryInfoBufferSize,
1751 pUrlEntry,
1752 TRUE /* UNICODE */);
1753 if (error != ERROR_SUCCESS)
1755 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1756 SetLastError(error);
1757 return FALSE;
1759 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1762 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1764 return TRUE;
1767 /***********************************************************************
1768 * GetUrlCacheEntryInfoExW (WININET.@)
1771 BOOL WINAPI GetUrlCacheEntryInfoExW(
1772 LPCWSTR lpszUrl,
1773 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1774 LPDWORD lpdwCacheEntryInfoBufSize,
1775 LPWSTR lpszReserved,
1776 LPDWORD lpdwReserved,
1777 LPVOID lpReserved,
1778 DWORD dwFlags)
1780 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1781 debugstr_w(lpszUrl),
1782 lpCacheEntryInfo,
1783 lpdwCacheEntryInfoBufSize,
1784 lpszReserved,
1785 lpdwReserved,
1786 lpReserved,
1787 dwFlags);
1789 if ((lpszReserved != NULL) ||
1790 (lpdwReserved != NULL) ||
1791 (lpReserved != NULL))
1793 ERR("Reserved value was not 0\n");
1794 SetLastError(ERROR_INVALID_PARAMETER);
1795 return FALSE;
1797 if (dwFlags != 0)
1799 FIXME("Undocumented flag(s): %x\n", dwFlags);
1800 SetLastError(ERROR_FILE_NOT_FOUND);
1801 return FALSE;
1803 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1806 /***********************************************************************
1807 * SetUrlCacheEntryInfoA (WININET.@)
1809 BOOL WINAPI SetUrlCacheEntryInfoA(
1810 LPCSTR lpszUrlName,
1811 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1812 DWORD dwFieldControl)
1814 LPURLCACHE_HEADER pHeader;
1815 struct _HASH_ENTRY * pHashEntry;
1816 CACHEFILE_ENTRY * pEntry;
1817 URLCACHECONTAINER * pContainer;
1818 DWORD error;
1820 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1822 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1823 if (error != ERROR_SUCCESS)
1825 SetLastError(error);
1826 return FALSE;
1829 error = URLCacheContainer_OpenIndex(pContainer);
1830 if (error != ERROR_SUCCESS)
1832 SetLastError(error);
1833 return FALSE;
1836 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1837 return FALSE;
1839 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1841 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1842 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1843 SetLastError(ERROR_FILE_NOT_FOUND);
1844 return FALSE;
1847 pEntry = (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((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1852 SetLastError(ERROR_FILE_NOT_FOUND);
1853 return FALSE;
1856 URLCache_SetEntryInfo(
1857 (URL_CACHEFILE_ENTRY *)pEntry,
1858 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1859 dwFieldControl);
1861 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1863 return TRUE;
1866 /***********************************************************************
1867 * SetUrlCacheEntryInfoW (WININET.@)
1869 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1871 LPURLCACHE_HEADER pHeader;
1872 struct _HASH_ENTRY * pHashEntry;
1873 CACHEFILE_ENTRY * pEntry;
1874 URLCACHECONTAINER * pContainer;
1875 DWORD error;
1877 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1879 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1880 if (error != ERROR_SUCCESS)
1882 SetLastError(error);
1883 return FALSE;
1886 error = URLCacheContainer_OpenIndex(pContainer);
1887 if (error != ERROR_SUCCESS)
1889 SetLastError(error);
1890 return FALSE;
1893 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1894 return FALSE;
1896 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1898 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1899 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1900 SetLastError(ERROR_FILE_NOT_FOUND);
1901 return FALSE;
1904 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1905 if (pEntry->dwSignature != URL_SIGNATURE)
1907 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1908 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1909 SetLastError(ERROR_FILE_NOT_FOUND);
1910 return FALSE;
1913 URLCache_SetEntryInfo(
1914 (URL_CACHEFILE_ENTRY *)pEntry,
1915 lpCacheEntryInfo,
1916 dwFieldControl);
1918 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1920 return TRUE;
1923 /***********************************************************************
1924 * RetrieveUrlCacheEntryFileA (WININET.@)
1927 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1928 IN LPCSTR lpszUrlName,
1929 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1930 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1931 IN DWORD dwReserved
1934 LPURLCACHE_HEADER pHeader;
1935 struct _HASH_ENTRY * pHashEntry;
1936 CACHEFILE_ENTRY * pEntry;
1937 URL_CACHEFILE_ENTRY * pUrlEntry;
1938 URLCACHECONTAINER * pContainer;
1939 DWORD error;
1941 TRACE("(%s, %p, %p, 0x%08x)\n",
1942 debugstr_a(lpszUrlName),
1943 lpCacheEntryInfo,
1944 lpdwCacheEntryInfoBufferSize,
1945 dwReserved);
1947 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1948 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1950 SetLastError(ERROR_INVALID_PARAMETER);
1951 return FALSE;
1954 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1955 if (error != ERROR_SUCCESS)
1957 SetLastError(error);
1958 return FALSE;
1961 error = URLCacheContainer_OpenIndex(pContainer);
1962 if (error != ERROR_SUCCESS)
1964 SetLastError(error);
1965 return FALSE;
1968 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1969 return FALSE;
1971 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1974 TRACE("entry %s not found!\n", lpszUrlName);
1975 SetLastError(ERROR_FILE_NOT_FOUND);
1976 return FALSE;
1979 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1980 if (pEntry->dwSignature != URL_SIGNATURE)
1982 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1983 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1984 SetLastError(ERROR_FILE_NOT_FOUND);
1985 return FALSE;
1988 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1989 if (!pUrlEntry->dwOffsetLocalName)
1991 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1992 SetLastError(ERROR_INVALID_DATA);
1993 return FALSE;
1996 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1997 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1999 pUrlEntry->dwHitRate++;
2000 pUrlEntry->dwUseCount++;
2001 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2002 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2004 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2005 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2006 FALSE);
2007 if (error != ERROR_SUCCESS)
2009 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2010 SetLastError(error);
2011 return FALSE;
2013 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2015 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2017 return TRUE;
2020 /***********************************************************************
2021 * RetrieveUrlCacheEntryFileW (WININET.@)
2024 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2025 IN LPCWSTR lpszUrlName,
2026 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2027 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2028 IN DWORD dwReserved
2031 LPURLCACHE_HEADER pHeader;
2032 struct _HASH_ENTRY * pHashEntry;
2033 CACHEFILE_ENTRY * pEntry;
2034 URL_CACHEFILE_ENTRY * pUrlEntry;
2035 URLCACHECONTAINER * pContainer;
2036 DWORD error;
2038 TRACE("(%s, %p, %p, 0x%08x)\n",
2039 debugstr_w(lpszUrlName),
2040 lpCacheEntryInfo,
2041 lpdwCacheEntryInfoBufferSize,
2042 dwReserved);
2044 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2045 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2047 SetLastError(ERROR_INVALID_PARAMETER);
2048 return FALSE;
2051 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2052 if (error != ERROR_SUCCESS)
2054 SetLastError(error);
2055 return FALSE;
2058 error = URLCacheContainer_OpenIndex(pContainer);
2059 if (error != ERROR_SUCCESS)
2061 SetLastError(error);
2062 return FALSE;
2065 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2066 return FALSE;
2068 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2070 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2071 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2072 SetLastError(ERROR_FILE_NOT_FOUND);
2073 return FALSE;
2076 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2077 if (pEntry->dwSignature != URL_SIGNATURE)
2079 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2080 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2081 SetLastError(ERROR_FILE_NOT_FOUND);
2082 return FALSE;
2085 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2086 if (!pUrlEntry->dwOffsetLocalName)
2088 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2089 SetLastError(ERROR_INVALID_DATA);
2090 return FALSE;
2093 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2094 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2096 pUrlEntry->dwHitRate++;
2097 pUrlEntry->dwUseCount++;
2098 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2099 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2101 error = URLCache_CopyEntry(
2102 pContainer,
2103 pHeader,
2104 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2105 lpdwCacheEntryInfoBufferSize,
2106 pUrlEntry,
2107 TRUE /* UNICODE */);
2108 if (error != ERROR_SUCCESS)
2110 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2111 SetLastError(error);
2112 return FALSE;
2114 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2116 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2118 return TRUE;
2121 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2122 struct _HASH_ENTRY *pHashEntry)
2124 CACHEFILE_ENTRY * pEntry;
2125 URL_CACHEFILE_ENTRY * pUrlEntry;
2127 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2128 if (pEntry->dwSignature != URL_SIGNATURE)
2130 FIXME("Trying to delete entry of unknown format %s\n",
2131 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2132 SetLastError(ERROR_FILE_NOT_FOUND);
2133 return FALSE;
2135 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2136 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2138 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2139 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2141 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2143 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2144 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2145 else
2146 pHeader->ExemptUsage.QuadPart = 0;
2148 else
2150 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2151 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2152 else
2153 pHeader->CacheUsage.QuadPart = 0;
2156 URLCache_DeleteEntry(pHeader, pEntry);
2158 URLCache_DeleteEntryFromHash(pHashEntry);
2159 return TRUE;
2162 /***********************************************************************
2163 * UnlockUrlCacheEntryFileA (WININET.@)
2166 BOOL WINAPI UnlockUrlCacheEntryFileA(
2167 IN LPCSTR lpszUrlName,
2168 IN DWORD dwReserved
2171 LPURLCACHE_HEADER pHeader;
2172 struct _HASH_ENTRY * pHashEntry;
2173 CACHEFILE_ENTRY * pEntry;
2174 URL_CACHEFILE_ENTRY * pUrlEntry;
2175 URLCACHECONTAINER * pContainer;
2176 DWORD error;
2178 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2180 if (dwReserved)
2182 ERR("dwReserved != 0\n");
2183 SetLastError(ERROR_INVALID_PARAMETER);
2184 return FALSE;
2187 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2188 if (error != ERROR_SUCCESS)
2190 SetLastError(error);
2191 return FALSE;
2194 error = URLCacheContainer_OpenIndex(pContainer);
2195 if (error != ERROR_SUCCESS)
2197 SetLastError(error);
2198 return FALSE;
2201 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2202 return FALSE;
2204 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2206 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2207 TRACE("entry %s not found!\n", lpszUrlName);
2208 SetLastError(ERROR_FILE_NOT_FOUND);
2209 return FALSE;
2212 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2213 if (pEntry->dwSignature != URL_SIGNATURE)
2215 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2216 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2217 SetLastError(ERROR_FILE_NOT_FOUND);
2218 return FALSE;
2221 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2223 if (pUrlEntry->dwUseCount == 0)
2225 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2226 return FALSE;
2228 pUrlEntry->dwUseCount--;
2229 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2231 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2233 return TRUE;
2236 /***********************************************************************
2237 * UnlockUrlCacheEntryFileW (WININET.@)
2240 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2242 LPURLCACHE_HEADER pHeader;
2243 struct _HASH_ENTRY * pHashEntry;
2244 CACHEFILE_ENTRY * pEntry;
2245 URL_CACHEFILE_ENTRY * pUrlEntry;
2246 URLCACHECONTAINER * pContainer;
2247 DWORD error;
2249 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2251 if (dwReserved)
2253 ERR("dwReserved != 0\n");
2254 SetLastError(ERROR_INVALID_PARAMETER);
2255 return FALSE;
2258 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2259 if (error != ERROR_SUCCESS)
2261 SetLastError(error);
2262 return FALSE;
2265 error = URLCacheContainer_OpenIndex(pContainer);
2266 if (error != ERROR_SUCCESS)
2268 SetLastError(error);
2269 return FALSE;
2272 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2273 return FALSE;
2275 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2277 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2278 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2279 SetLastError(ERROR_FILE_NOT_FOUND);
2280 return FALSE;
2283 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2284 if (pEntry->dwSignature != URL_SIGNATURE)
2286 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2287 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2288 SetLastError(ERROR_FILE_NOT_FOUND);
2289 return FALSE;
2292 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2294 if (pUrlEntry->dwUseCount == 0)
2296 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2297 return FALSE;
2299 pUrlEntry->dwUseCount--;
2300 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2302 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2304 return TRUE;
2307 /***********************************************************************
2308 * CreateUrlCacheEntryA (WININET.@)
2311 BOOL WINAPI CreateUrlCacheEntryA(
2312 IN LPCSTR lpszUrlName,
2313 IN DWORD dwExpectedFileSize,
2314 IN LPCSTR lpszFileExtension,
2315 OUT LPSTR lpszFileName,
2316 IN DWORD dwReserved
2319 WCHAR *url_name;
2320 WCHAR *file_extension = NULL;
2321 WCHAR file_name[MAX_PATH];
2322 BOOL bSuccess = FALSE;
2323 DWORD dwError = 0;
2325 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2326 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2328 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2330 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2332 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2334 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2336 bSuccess = TRUE;
2338 else
2340 dwError = GetLastError();
2343 else
2345 dwError = GetLastError();
2347 HeapFree(GetProcessHeap(), 0, file_extension);
2349 else
2351 dwError = GetLastError();
2353 HeapFree(GetProcessHeap(), 0, url_name);
2354 if (!bSuccess)
2355 SetLastError(dwError);
2357 return bSuccess;
2359 /***********************************************************************
2360 * CreateUrlCacheEntryW (WININET.@)
2363 BOOL WINAPI CreateUrlCacheEntryW(
2364 IN LPCWSTR lpszUrlName,
2365 IN DWORD dwExpectedFileSize,
2366 IN LPCWSTR lpszFileExtension,
2367 OUT LPWSTR lpszFileName,
2368 IN DWORD dwReserved
2371 URLCACHECONTAINER * pContainer;
2372 LPURLCACHE_HEADER pHeader;
2373 CHAR szFile[MAX_PATH];
2374 WCHAR szExtension[MAX_PATH];
2375 LPCWSTR lpszUrlPart;
2376 LPCWSTR lpszUrlEnd;
2377 LPCWSTR lpszFileNameExtension;
2378 LPWSTR lpszFileNameNoPath;
2379 int i;
2380 int countnoextension;
2381 BYTE CacheDir;
2382 LONG lBufferSize;
2383 BOOL bFound = FALSE;
2384 int count;
2385 DWORD error;
2386 HANDLE hFile;
2387 FILETIME ft;
2389 static const WCHAR szWWW[] = {'w','w','w',0};
2390 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2392 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2393 debugstr_w(lpszUrlName),
2394 dwExpectedFileSize,
2395 debugstr_w(lpszFileExtension),
2396 lpszFileName,
2397 dwReserved);
2399 if (dwReserved)
2400 FIXME("dwReserved 0x%08x\n", dwReserved);
2402 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2404 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2405 lpszUrlEnd--;
2407 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2408 if (!lpszUrlPart)
2409 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2410 if (lpszUrlPart)
2411 lpszUrlEnd = lpszUrlPart;
2413 for (lpszUrlPart = lpszUrlEnd;
2414 (lpszUrlPart >= lpszUrlName);
2415 lpszUrlPart--)
2417 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2419 bFound = TRUE;
2420 lpszUrlPart++;
2421 break;
2424 if (!lstrcmpW(lpszUrlPart, szWWW))
2426 lpszUrlPart += lstrlenW(szWWW);
2429 count = lpszUrlEnd - lpszUrlPart;
2431 if (bFound && (count < MAX_PATH))
2433 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2434 if (!len)
2435 return FALSE;
2436 szFile[len] = '\0';
2437 while(len && szFile[--len] == '/') szFile[len] = '\0';
2439 /* FIXME: get rid of illegal characters like \, / and : */
2441 else
2443 FIXME("need to generate a random filename\n");
2446 TRACE("File name: %s\n", debugstr_a(szFile));
2448 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2449 if (error != ERROR_SUCCESS)
2451 SetLastError(error);
2452 return FALSE;
2455 error = URLCacheContainer_OpenIndex(pContainer);
2456 if (error != ERROR_SUCCESS)
2458 SetLastError(error);
2459 return FALSE;
2462 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2463 return FALSE;
2465 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2467 lBufferSize = MAX_PATH * sizeof(WCHAR);
2468 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2470 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2471 debugstr_a(szFile), lBufferSize);
2472 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2473 return FALSE;
2476 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2478 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2479 lpszFileNameNoPath >= lpszFileName;
2480 --lpszFileNameNoPath)
2482 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2483 break;
2486 countnoextension = lstrlenW(lpszFileNameNoPath);
2487 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2488 if (lpszFileNameExtension)
2489 countnoextension -= lstrlenW(lpszFileNameExtension);
2490 *szExtension = '\0';
2492 if (lpszFileExtension)
2494 szExtension[0] = '.';
2495 lstrcpyW(szExtension+1, lpszFileExtension);
2498 for (i = 0; i < 255; i++)
2500 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2501 WCHAR *p;
2503 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2504 for (p = lpszFileNameNoPath + 1; *p; p++)
2506 switch (*p)
2508 case '<': case '>':
2509 case ':': case '"':
2510 case '/': case '\\':
2511 case '|': case '?':
2512 case '*':
2513 *p = '_'; break;
2514 default: break;
2517 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2519 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2520 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2521 if (hFile != INVALID_HANDLE_VALUE)
2523 CloseHandle(hFile);
2524 return TRUE;
2528 GetSystemTimeAsFileTime(&ft);
2529 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2531 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2532 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2533 if (hFile != INVALID_HANDLE_VALUE)
2535 CloseHandle(hFile);
2536 return TRUE;
2539 WARN("Could not find a unique filename\n");
2540 return FALSE;
2544 /***********************************************************************
2545 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2547 * The bug we are compensating for is that some drongo at Microsoft
2548 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2549 * As a consequence, CommitUrlCacheEntryA has been effectively
2550 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2551 * is still defined as LPCWSTR. The result (other than madness) is
2552 * that we always need to store lpHeaderInfo in CP_ACP rather than
2553 * in UTF16, and we need to avoid converting lpHeaderInfo in
2554 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2555 * result will lose data for arbitrary binary data.
2558 static BOOL CommitUrlCacheEntryInternal(
2559 IN LPCWSTR lpszUrlName,
2560 IN LPCWSTR lpszLocalFileName,
2561 IN FILETIME ExpireTime,
2562 IN FILETIME LastModifiedTime,
2563 IN DWORD CacheEntryType,
2564 IN LPBYTE lpHeaderInfo,
2565 IN DWORD dwHeaderSize,
2566 IN LPCWSTR lpszFileExtension,
2567 IN LPCWSTR lpszOriginalUrl
2570 URLCACHECONTAINER * pContainer;
2571 LPURLCACHE_HEADER pHeader;
2572 struct _HASH_ENTRY * pHashEntry;
2573 CACHEFILE_ENTRY * pEntry;
2574 URL_CACHEFILE_ENTRY * pUrlEntry;
2575 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2576 DWORD dwOffsetLocalFileName = 0;
2577 DWORD dwOffsetHeader = 0;
2578 DWORD dwOffsetFileExtension = 0;
2579 LARGE_INTEGER file_size;
2580 BYTE cDirectory = 0;
2581 char achFile[MAX_PATH];
2582 LPSTR lpszUrlNameA = NULL;
2583 LPSTR lpszFileExtensionA = NULL;
2584 char *pchLocalFileName = 0;
2585 DWORD error;
2587 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2588 debugstr_w(lpszUrlName),
2589 debugstr_w(lpszLocalFileName),
2590 CacheEntryType,
2591 lpHeaderInfo,
2592 dwHeaderSize,
2593 debugstr_w(lpszFileExtension),
2594 debugstr_w(lpszOriginalUrl));
2596 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2598 SetLastError(ERROR_INVALID_PARAMETER);
2599 return FALSE;
2601 if (lpszOriginalUrl)
2602 WARN(": lpszOriginalUrl ignored\n");
2604 file_size.QuadPart = 0;
2605 if (lpszLocalFileName)
2607 HANDLE hFile;
2609 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2610 if (hFile == INVALID_HANDLE_VALUE)
2612 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2613 return FALSE;
2616 /* Get file size */
2617 if (!GetFileSizeEx(hFile, &file_size))
2619 ERR("couldn't get file size (error is %d)\n", GetLastError());
2620 CloseHandle(hFile);
2621 return FALSE;
2624 CloseHandle(hFile);
2627 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2628 if (error != ERROR_SUCCESS)
2630 SetLastError(error);
2631 return FALSE;
2634 error = URLCacheContainer_OpenIndex(pContainer);
2635 if (error != ERROR_SUCCESS)
2637 SetLastError(error);
2638 return FALSE;
2641 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2642 return FALSE;
2644 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2645 if (!lpszUrlNameA)
2647 error = GetLastError();
2648 goto cleanup;
2651 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2653 error = GetLastError();
2654 goto cleanup;
2657 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2659 FIXME("entry already in cache - don't know what to do!\n");
2661 * SetLastError(ERROR_FILE_NOT_FOUND);
2662 * return FALSE;
2664 goto cleanup;
2667 if (lpszLocalFileName)
2669 BOOL bFound = FALSE;
2671 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2673 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2674 error = ERROR_INVALID_PARAMETER;
2675 goto cleanup;
2678 /* skip container path prefix */
2679 lpszLocalFileName += lstrlenW(pContainer->path);
2681 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2682 pchLocalFileName = achFile;
2684 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2686 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2688 bFound = TRUE;
2689 break;
2693 if (!bFound)
2695 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2696 error = ERROR_INVALID_PARAMETER;
2697 goto cleanup;
2700 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2703 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2704 if (lpszLocalFileName)
2706 dwOffsetLocalFileName = dwBytesNeeded;
2707 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2709 if (lpHeaderInfo)
2711 dwOffsetHeader = dwBytesNeeded;
2712 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2714 if (lpszFileExtensionA)
2716 dwOffsetFileExtension = dwBytesNeeded;
2717 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2720 /* round up to next block */
2721 if (dwBytesNeeded % BLOCKSIZE)
2723 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2724 dwBytesNeeded += BLOCKSIZE;
2727 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2729 ERR("no free entries\n");
2730 error = ERROR_DISK_FULL;
2731 goto cleanup;
2734 /* FindFirstFreeEntry fills in blocks used */
2735 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2736 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2737 pUrlEntry->CacheDir = cDirectory;
2738 pUrlEntry->CacheEntryType = CacheEntryType;
2739 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2740 if (CacheEntryType & STICKY_CACHE_ENTRY)
2742 /* Sticky entries have a default exempt time of one day */
2743 pUrlEntry->dwExemptDelta = 86400;
2745 else
2746 pUrlEntry->dwExemptDelta = 0;
2747 pUrlEntry->dwHitRate = 0;
2748 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2749 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2750 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2751 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2752 pUrlEntry->size.QuadPart = file_size.QuadPart;
2753 pUrlEntry->dwUseCount = 0;
2754 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2755 pUrlEntry->LastModifiedTime = LastModifiedTime;
2756 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2757 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2758 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2759 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2761 /*** Unknowns ***/
2762 pUrlEntry->dwUnknown1 = 0;
2763 pUrlEntry->dwUnknown2 = 0;
2764 pUrlEntry->dwUnknown3 = 0x60;
2765 pUrlEntry->Unknown4 = 0;
2766 pUrlEntry->wUnknown5 = 0x1010;
2767 pUrlEntry->dwUnknown7 = 0;
2768 pUrlEntry->dwUnknown8 = 0;
2771 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2772 if (dwOffsetLocalFileName)
2773 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2774 if (dwOffsetHeader)
2775 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2776 if (dwOffsetFileExtension)
2777 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2779 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2780 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2781 if (error != ERROR_SUCCESS)
2782 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2783 else
2785 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2786 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2787 if (CacheEntryType & STICKY_CACHE_ENTRY)
2788 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2789 else
2790 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2791 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2792 pHeader->CacheLimit.QuadPart)
2793 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2796 cleanup:
2797 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2798 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2799 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2801 if (error == ERROR_SUCCESS)
2802 return TRUE;
2803 else
2805 SetLastError(error);
2806 return FALSE;
2810 /***********************************************************************
2811 * CommitUrlCacheEntryA (WININET.@)
2814 BOOL WINAPI CommitUrlCacheEntryA(
2815 IN LPCSTR lpszUrlName,
2816 IN LPCSTR lpszLocalFileName,
2817 IN FILETIME ExpireTime,
2818 IN FILETIME LastModifiedTime,
2819 IN DWORD CacheEntryType,
2820 IN LPBYTE lpHeaderInfo,
2821 IN DWORD dwHeaderSize,
2822 IN LPCSTR lpszFileExtension,
2823 IN LPCSTR lpszOriginalUrl
2826 WCHAR *url_name = NULL;
2827 WCHAR *local_file_name = NULL;
2828 WCHAR *original_url = NULL;
2829 WCHAR *file_extension = NULL;
2830 BOOL bSuccess = FALSE;
2832 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2833 debugstr_a(lpszUrlName),
2834 debugstr_a(lpszLocalFileName),
2835 CacheEntryType,
2836 lpHeaderInfo,
2837 dwHeaderSize,
2838 debugstr_a(lpszFileExtension),
2839 debugstr_a(lpszOriginalUrl));
2841 url_name = heap_strdupAtoW(lpszUrlName);
2842 if (!url_name)
2843 goto cleanup;
2845 if (lpszLocalFileName)
2847 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2848 if (!local_file_name)
2849 goto cleanup;
2851 if (lpszFileExtension)
2853 file_extension = heap_strdupAtoW(lpszFileExtension);
2854 if (!file_extension)
2855 goto cleanup;
2857 if (lpszOriginalUrl)
2859 original_url = heap_strdupAtoW(lpszOriginalUrl);
2860 if (!original_url)
2861 goto cleanup;
2864 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2865 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2866 file_extension, original_url);
2868 cleanup:
2869 HeapFree(GetProcessHeap(), 0, original_url);
2870 HeapFree(GetProcessHeap(), 0, file_extension);
2871 HeapFree(GetProcessHeap(), 0, local_file_name);
2872 HeapFree(GetProcessHeap(), 0, url_name);
2874 return bSuccess;
2877 /***********************************************************************
2878 * CommitUrlCacheEntryW (WININET.@)
2881 BOOL WINAPI CommitUrlCacheEntryW(
2882 IN LPCWSTR lpszUrlName,
2883 IN LPCWSTR lpszLocalFileName,
2884 IN FILETIME ExpireTime,
2885 IN FILETIME LastModifiedTime,
2886 IN DWORD CacheEntryType,
2887 IN LPWSTR lpHeaderInfo,
2888 IN DWORD dwHeaderSize,
2889 IN LPCWSTR lpszFileExtension,
2890 IN LPCWSTR lpszOriginalUrl
2893 DWORD dwError = 0;
2894 BOOL bSuccess = FALSE;
2895 DWORD len = 0;
2896 CHAR *header_info = NULL;
2898 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2899 debugstr_w(lpszUrlName),
2900 debugstr_w(lpszLocalFileName),
2901 CacheEntryType,
2902 lpHeaderInfo,
2903 dwHeaderSize,
2904 debugstr_w(lpszFileExtension),
2905 debugstr_w(lpszOriginalUrl));
2907 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2909 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2910 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2912 bSuccess = TRUE;
2914 else
2916 dwError = GetLastError();
2918 if (header_info)
2920 HeapFree(GetProcessHeap(), 0, header_info);
2921 if (!bSuccess)
2922 SetLastError(dwError);
2925 return bSuccess;
2928 /***********************************************************************
2929 * ReadUrlCacheEntryStream (WININET.@)
2932 BOOL WINAPI ReadUrlCacheEntryStream(
2933 IN HANDLE hUrlCacheStream,
2934 IN DWORD dwLocation,
2935 IN OUT LPVOID lpBuffer,
2936 IN OUT LPDWORD lpdwLen,
2937 IN DWORD dwReserved
2940 /* Get handle to file from 'stream' */
2941 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2943 if (dwReserved != 0)
2945 ERR("dwReserved != 0\n");
2946 SetLastError(ERROR_INVALID_PARAMETER);
2947 return FALSE;
2950 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2952 SetLastError(ERROR_INVALID_HANDLE);
2953 return FALSE;
2956 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2957 return FALSE;
2958 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2961 /***********************************************************************
2962 * RetrieveUrlCacheEntryStreamA (WININET.@)
2965 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2966 IN LPCSTR lpszUrlName,
2967 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2968 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2969 IN BOOL fRandomRead,
2970 IN DWORD dwReserved
2973 /* NOTE: this is not the same as the way that the native
2974 * version allocates 'stream' handles. I did it this way
2975 * as it is much easier and no applications should depend
2976 * on this behaviour. (Native version appears to allocate
2977 * indices into a table)
2979 STREAM_HANDLE * pStream;
2980 HANDLE hFile;
2982 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2983 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2985 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2986 lpCacheEntryInfo,
2987 lpdwCacheEntryInfoBufferSize,
2988 dwReserved))
2990 return NULL;
2993 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2994 GENERIC_READ,
2995 FILE_SHARE_READ,
2996 NULL,
2997 OPEN_EXISTING,
2998 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2999 NULL);
3000 if (hFile == INVALID_HANDLE_VALUE)
3001 return FALSE;
3003 /* allocate handle storage space */
3004 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3005 if (!pStream)
3007 CloseHandle(hFile);
3008 SetLastError(ERROR_OUTOFMEMORY);
3009 return FALSE;
3012 pStream->hFile = hFile;
3013 strcpy(pStream->lpszUrl, lpszUrlName);
3014 return pStream;
3017 /***********************************************************************
3018 * RetrieveUrlCacheEntryStreamW (WININET.@)
3021 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3022 IN LPCWSTR lpszUrlName,
3023 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3024 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3025 IN BOOL fRandomRead,
3026 IN DWORD dwReserved
3029 DWORD size;
3030 int url_len;
3031 /* NOTE: this is not the same as the way that the native
3032 * version allocates 'stream' handles. I did it this way
3033 * as it is much easier and no applications should depend
3034 * on this behaviour. (Native version appears to allocate
3035 * indices into a table)
3037 STREAM_HANDLE * pStream;
3038 HANDLE hFile;
3040 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3041 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3043 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3044 lpCacheEntryInfo,
3045 lpdwCacheEntryInfoBufferSize,
3046 dwReserved))
3048 return NULL;
3051 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3052 GENERIC_READ,
3053 FILE_SHARE_READ,
3054 NULL,
3055 OPEN_EXISTING,
3056 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3057 NULL);
3058 if (hFile == INVALID_HANDLE_VALUE)
3059 return FALSE;
3061 /* allocate handle storage space */
3062 size = sizeof(STREAM_HANDLE);
3063 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3064 size += url_len;
3065 pStream = HeapAlloc(GetProcessHeap(), 0, size);
3066 if (!pStream)
3068 CloseHandle(hFile);
3069 SetLastError(ERROR_OUTOFMEMORY);
3070 return FALSE;
3073 pStream->hFile = hFile;
3074 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3075 return pStream;
3078 /***********************************************************************
3079 * UnlockUrlCacheEntryStream (WININET.@)
3082 BOOL WINAPI UnlockUrlCacheEntryStream(
3083 IN HANDLE hUrlCacheStream,
3084 IN DWORD dwReserved
3087 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3089 if (dwReserved != 0)
3091 ERR("dwReserved != 0\n");
3092 SetLastError(ERROR_INVALID_PARAMETER);
3093 return FALSE;
3096 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3098 SetLastError(ERROR_INVALID_HANDLE);
3099 return FALSE;
3102 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3103 return FALSE;
3105 /* close file handle */
3106 CloseHandle(pStream->hFile);
3108 /* free allocated space */
3109 HeapFree(GetProcessHeap(), 0, pStream);
3111 return TRUE;
3115 /***********************************************************************
3116 * DeleteUrlCacheEntryA (WININET.@)
3119 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3121 URLCACHECONTAINER * pContainer;
3122 LPURLCACHE_HEADER pHeader;
3123 struct _HASH_ENTRY * pHashEntry;
3124 DWORD error;
3125 BOOL ret;
3127 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3129 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3130 if (error != ERROR_SUCCESS)
3132 SetLastError(error);
3133 return FALSE;
3136 error = URLCacheContainer_OpenIndex(pContainer);
3137 if (error != ERROR_SUCCESS)
3139 SetLastError(error);
3140 return FALSE;
3143 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3144 return FALSE;
3146 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3148 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3149 TRACE("entry %s not found!\n", lpszUrlName);
3150 SetLastError(ERROR_FILE_NOT_FOUND);
3151 return FALSE;
3154 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3156 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3158 return ret;
3161 /***********************************************************************
3162 * DeleteUrlCacheEntryW (WININET.@)
3165 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3167 URLCACHECONTAINER * pContainer;
3168 LPURLCACHE_HEADER pHeader;
3169 struct _HASH_ENTRY * pHashEntry;
3170 LPSTR urlA;
3171 DWORD error;
3172 BOOL ret;
3174 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3176 urlA = heap_strdupWtoA(lpszUrlName);
3177 if (!urlA)
3179 SetLastError(ERROR_OUTOFMEMORY);
3180 return FALSE;
3183 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3184 if (error != ERROR_SUCCESS)
3186 HeapFree(GetProcessHeap(), 0, urlA);
3187 SetLastError(error);
3188 return FALSE;
3191 error = URLCacheContainer_OpenIndex(pContainer);
3192 if (error != ERROR_SUCCESS)
3194 HeapFree(GetProcessHeap(), 0, urlA);
3195 SetLastError(error);
3196 return FALSE;
3199 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3201 HeapFree(GetProcessHeap(), 0, urlA);
3202 return FALSE;
3205 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3207 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3208 TRACE("entry %s not found!\n", debugstr_a(urlA));
3209 HeapFree(GetProcessHeap(), 0, urlA);
3210 SetLastError(ERROR_FILE_NOT_FOUND);
3211 return FALSE;
3214 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3216 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3218 HeapFree(GetProcessHeap(), 0, urlA);
3219 return ret;
3222 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3224 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3225 return TRUE;
3228 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3230 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3231 return TRUE;
3234 /***********************************************************************
3235 * CreateCacheContainerA (WININET.@)
3237 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3238 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3240 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3241 d1, d2, d3, d4, d5, d6, d7, d8);
3242 return TRUE;
3245 /***********************************************************************
3246 * CreateCacheContainerW (WININET.@)
3248 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3249 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3251 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3252 d1, d2, d3, d4, d5, d6, d7, d8);
3253 return TRUE;
3256 /***********************************************************************
3257 * FindFirstUrlCacheContainerA (WININET.@)
3259 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3261 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3262 return NULL;
3265 /***********************************************************************
3266 * FindFirstUrlCacheContainerW (WININET.@)
3268 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3270 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3271 return NULL;
3274 /***********************************************************************
3275 * FindNextUrlCacheContainerA (WININET.@)
3277 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3279 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3280 return FALSE;
3283 /***********************************************************************
3284 * FindNextUrlCacheContainerW (WININET.@)
3286 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3288 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3289 return FALSE;
3292 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3293 LPCSTR lpszUrlSearchPattern,
3294 DWORD dwFlags,
3295 DWORD dwFilter,
3296 GROUPID GroupId,
3297 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3298 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3299 LPVOID lpReserved,
3300 LPDWORD pcbReserved2,
3301 LPVOID lpReserved3
3304 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3305 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3306 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3307 SetLastError(ERROR_FILE_NOT_FOUND);
3308 return NULL;
3311 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3312 LPCWSTR lpszUrlSearchPattern,
3313 DWORD dwFlags,
3314 DWORD dwFilter,
3315 GROUPID GroupId,
3316 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3317 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3318 LPVOID lpReserved,
3319 LPDWORD pcbReserved2,
3320 LPVOID lpReserved3
3323 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3324 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3325 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3326 SetLastError(ERROR_FILE_NOT_FOUND);
3327 return NULL;
3330 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3332 typedef struct URLCacheFindEntryHandle
3334 DWORD dwMagic;
3335 LPWSTR lpszUrlSearchPattern;
3336 DWORD dwContainerIndex;
3337 DWORD dwHashTableIndex;
3338 DWORD dwHashEntryIndex;
3339 } URLCacheFindEntryHandle;
3341 /***********************************************************************
3342 * FindFirstUrlCacheEntryA (WININET.@)
3345 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3346 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3348 URLCacheFindEntryHandle *pEntryHandle;
3350 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3352 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3353 if (!pEntryHandle)
3354 return NULL;
3356 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3357 if (lpszUrlSearchPattern)
3359 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3360 if (!pEntryHandle->lpszUrlSearchPattern)
3362 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3363 return NULL;
3366 else
3367 pEntryHandle->lpszUrlSearchPattern = NULL;
3368 pEntryHandle->dwContainerIndex = 0;
3369 pEntryHandle->dwHashTableIndex = 0;
3370 pEntryHandle->dwHashEntryIndex = 0;
3372 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3374 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3375 return NULL;
3377 return pEntryHandle;
3380 /***********************************************************************
3381 * FindFirstUrlCacheEntryW (WININET.@)
3384 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3385 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3387 URLCacheFindEntryHandle *pEntryHandle;
3389 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3391 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3392 if (!pEntryHandle)
3393 return NULL;
3395 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3396 if (lpszUrlSearchPattern)
3398 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3399 if (!pEntryHandle->lpszUrlSearchPattern)
3401 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3402 return NULL;
3405 else
3406 pEntryHandle->lpszUrlSearchPattern = NULL;
3407 pEntryHandle->dwContainerIndex = 0;
3408 pEntryHandle->dwHashTableIndex = 0;
3409 pEntryHandle->dwHashEntryIndex = 0;
3411 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3413 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3414 return NULL;
3416 return pEntryHandle;
3419 static BOOL FindNextUrlCacheEntryInternal(
3420 HANDLE hEnumHandle,
3421 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3422 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3423 BOOL unicode)
3425 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3426 URLCACHECONTAINER * pContainer;
3428 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3430 SetLastError(ERROR_INVALID_HANDLE);
3431 return FALSE;
3434 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3435 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3437 LPURLCACHE_HEADER pHeader;
3438 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3439 DWORD error;
3441 error = URLCacheContainer_OpenIndex(pContainer);
3442 if (error != ERROR_SUCCESS)
3444 SetLastError(error);
3445 return FALSE;
3448 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3449 return FALSE;
3451 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3452 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3454 const struct _HASH_ENTRY *pHashEntry = NULL;
3455 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3456 pEntryHandle->dwHashEntryIndex++)
3458 const URL_CACHEFILE_ENTRY *pUrlEntry;
3459 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3461 if (pEntry->dwSignature != URL_SIGNATURE)
3462 continue;
3464 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3465 TRACE("Found URL: %s\n",
3466 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3467 TRACE("Header info: %s\n",
3468 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3470 error = URLCache_CopyEntry(
3471 pContainer,
3472 pHeader,
3473 lpNextCacheEntryInfo,
3474 lpdwNextCacheEntryInfoBufferSize,
3475 pUrlEntry,
3476 unicode);
3477 if (error != ERROR_SUCCESS)
3479 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3480 SetLastError(error);
3481 return FALSE;
3483 TRACE("Local File Name: %s\n",
3484 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3486 /* increment the current index so that next time the function
3487 * is called the next entry is returned */
3488 pEntryHandle->dwHashEntryIndex++;
3489 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3490 return TRUE;
3494 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3497 SetLastError(ERROR_NO_MORE_ITEMS);
3498 return FALSE;
3501 /***********************************************************************
3502 * FindNextUrlCacheEntryA (WININET.@)
3504 BOOL WINAPI FindNextUrlCacheEntryA(
3505 HANDLE hEnumHandle,
3506 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3507 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3509 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3511 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3512 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3515 /***********************************************************************
3516 * FindNextUrlCacheEntryW (WININET.@)
3518 BOOL WINAPI FindNextUrlCacheEntryW(
3519 HANDLE hEnumHandle,
3520 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3521 LPDWORD lpdwNextCacheEntryInfoBufferSize
3524 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3526 return FindNextUrlCacheEntryInternal(hEnumHandle,
3527 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3528 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3531 /***********************************************************************
3532 * FindCloseUrlCache (WININET.@)
3534 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3536 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3538 TRACE("(%p)\n", hEnumHandle);
3540 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3542 SetLastError(ERROR_INVALID_HANDLE);
3543 return FALSE;
3546 pEntryHandle->dwMagic = 0;
3547 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3548 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3550 return TRUE;
3553 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3554 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3556 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3557 dwSearchCondition, lpGroupId, lpReserved);
3558 return NULL;
3561 BOOL WINAPI FindNextUrlCacheEntryExA(
3562 HANDLE hEnumHandle,
3563 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3564 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3565 LPVOID lpReserved,
3566 LPDWORD pcbReserved2,
3567 LPVOID lpReserved3
3570 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3571 lpReserved, pcbReserved2, lpReserved3);
3572 return FALSE;
3575 BOOL WINAPI FindNextUrlCacheEntryExW(
3576 HANDLE hEnumHandle,
3577 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3578 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3579 LPVOID lpReserved,
3580 LPDWORD pcbReserved2,
3581 LPVOID lpReserved3
3584 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3585 lpReserved, pcbReserved2, lpReserved3);
3586 return FALSE;
3589 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3591 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3592 return FALSE;
3595 /***********************************************************************
3596 * CreateUrlCacheGroup (WININET.@)
3599 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3601 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3602 return FALSE;
3605 /***********************************************************************
3606 * DeleteUrlCacheGroup (WININET.@)
3609 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3611 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3612 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3613 return FALSE;
3616 /***********************************************************************
3617 * SetUrlCacheEntryGroupA (WININET.@)
3620 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3621 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3622 LPVOID lpReserved)
3624 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3625 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3626 pbGroupAttributes, cbGroupAttributes, lpReserved);
3627 SetLastError(ERROR_FILE_NOT_FOUND);
3628 return FALSE;
3631 /***********************************************************************
3632 * SetUrlCacheEntryGroupW (WININET.@)
3635 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3636 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3637 LPVOID lpReserved)
3639 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3640 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3641 pbGroupAttributes, cbGroupAttributes, lpReserved);
3642 SetLastError(ERROR_FILE_NOT_FOUND);
3643 return FALSE;
3646 /***********************************************************************
3647 * GetUrlCacheConfigInfoW (WININET.@)
3649 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3651 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3652 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3653 return FALSE;
3656 /***********************************************************************
3657 * GetUrlCacheConfigInfoA (WININET.@)
3659 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3661 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3662 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3663 return FALSE;
3666 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3667 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3668 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3670 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3671 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3672 lpdwGroupInfo, lpReserved);
3673 return FALSE;
3676 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3677 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3678 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3680 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3681 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3682 lpdwGroupInfo, lpReserved);
3683 return FALSE;
3686 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3687 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3689 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3690 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3691 return TRUE;
3694 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3695 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3697 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3698 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3699 return TRUE;
3702 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3704 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3705 return TRUE;
3708 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3710 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3711 return TRUE;
3714 /***********************************************************************
3715 * DeleteIE3Cache (WININET.@)
3717 * Deletes the files used by the IE3 URL caching system.
3719 * PARAMS
3720 * hWnd [I] A dummy window.
3721 * hInst [I] Instance of process calling the function.
3722 * lpszCmdLine [I] Options used by function.
3723 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3725 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3727 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3728 return 0;
3731 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3732 FILETIME *pftLastModified)
3734 BOOL ret;
3735 FILETIME now, expired;
3737 *pftLastModified = pUrlEntry->LastModifiedTime;
3738 GetSystemTimeAsFileTime(&now);
3739 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3740 pUrlEntry->wExpiredTime, &expired);
3741 /* If the expired time is 0, it's interpreted as not expired */
3742 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3743 ret = FALSE;
3744 else
3745 ret = CompareFileTime(&expired, &now) < 0;
3746 return ret;
3749 /***********************************************************************
3750 * IsUrlCacheEntryExpiredA (WININET.@)
3752 * PARAMS
3753 * url [I] Url
3754 * dwFlags [I] Unknown
3755 * pftLastModified [O] Last modified time
3757 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3759 LPURLCACHE_HEADER pHeader;
3760 struct _HASH_ENTRY * pHashEntry;
3761 const CACHEFILE_ENTRY * pEntry;
3762 const URL_CACHEFILE_ENTRY * pUrlEntry;
3763 URLCACHECONTAINER * pContainer;
3764 BOOL expired;
3766 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3768 if (!url || !pftLastModified)
3769 return TRUE;
3770 if (dwFlags)
3771 FIXME("unknown flags 0x%08x\n", dwFlags);
3773 /* Any error implies that the URL is expired, i.e. not in the cache */
3774 if (URLCacheContainers_FindContainerA(url, &pContainer))
3776 memset(pftLastModified, 0, sizeof(*pftLastModified));
3777 return TRUE;
3780 if (URLCacheContainer_OpenIndex(pContainer))
3782 memset(pftLastModified, 0, sizeof(*pftLastModified));
3783 return TRUE;
3786 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3788 memset(pftLastModified, 0, sizeof(*pftLastModified));
3789 return TRUE;
3792 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3794 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3795 memset(pftLastModified, 0, sizeof(*pftLastModified));
3796 TRACE("entry %s not found!\n", url);
3797 return TRUE;
3800 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3801 if (pEntry->dwSignature != URL_SIGNATURE)
3803 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3804 memset(pftLastModified, 0, sizeof(*pftLastModified));
3805 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3806 return TRUE;
3809 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3810 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3812 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3814 return expired;
3817 /***********************************************************************
3818 * IsUrlCacheEntryExpiredW (WININET.@)
3820 * PARAMS
3821 * url [I] Url
3822 * dwFlags [I] Unknown
3823 * pftLastModified [O] Last modified time
3825 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3827 LPURLCACHE_HEADER pHeader;
3828 struct _HASH_ENTRY * pHashEntry;
3829 const CACHEFILE_ENTRY * pEntry;
3830 const URL_CACHEFILE_ENTRY * pUrlEntry;
3831 URLCACHECONTAINER * pContainer;
3832 BOOL expired;
3834 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3836 if (!url || !pftLastModified)
3837 return TRUE;
3838 if (dwFlags)
3839 FIXME("unknown flags 0x%08x\n", dwFlags);
3841 /* Any error implies that the URL is expired, i.e. not in the cache */
3842 if (URLCacheContainers_FindContainerW(url, &pContainer))
3844 memset(pftLastModified, 0, sizeof(*pftLastModified));
3845 return TRUE;
3848 if (URLCacheContainer_OpenIndex(pContainer))
3850 memset(pftLastModified, 0, sizeof(*pftLastModified));
3851 return TRUE;
3854 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3856 memset(pftLastModified, 0, sizeof(*pftLastModified));
3857 return TRUE;
3860 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3862 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3863 memset(pftLastModified, 0, sizeof(*pftLastModified));
3864 TRACE("entry %s not found!\n", debugstr_w(url));
3865 return TRUE;
3868 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3870 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3871 memset(pftLastModified, 0, sizeof(*pftLastModified));
3872 TRACE("entry %s not found!\n", debugstr_w(url));
3873 return TRUE;
3876 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3877 if (pEntry->dwSignature != URL_SIGNATURE)
3879 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3880 memset(pftLastModified, 0, sizeof(*pftLastModified));
3881 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3882 return TRUE;
3885 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3886 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3888 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3890 return expired;
3893 /***********************************************************************
3894 * GetDiskInfoA (WININET.@)
3896 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3898 BOOL ret;
3899 ULARGE_INTEGER bytes_free, bytes_total;
3901 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3903 if (!path)
3905 SetLastError(ERROR_INVALID_PARAMETER);
3906 return FALSE;
3909 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3911 if (cluster_size) *cluster_size = 1;
3912 if (free) *free = bytes_free.QuadPart;
3913 if (total) *total = bytes_total.QuadPart;
3915 return ret;
3918 /***********************************************************************
3919 * RegisterUrlCacheNotification (WININET.@)
3921 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3923 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3924 return 0;
3927 /***********************************************************************
3928 * IncrementUrlCacheHeaderData (WININET.@)
3930 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3932 FIXME("(%u, %p)\n", index, data);
3933 return FALSE;