winecoreaudio: Fix a leak.
[wine/multimedia.git] / dlls / wininet / urlcache.c
blobcec5bbbfe5ac3e090de8fa834a5fc095360ef653
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 WaitForSingleObject(pContainer->hMutex, INFINITE);
229 if (pContainer->hMapping) {
230 ReleaseMutex(pContainer->hMutex);
231 return ERROR_SUCCESS;
234 strcpyW(wszFilePath, pContainer->path);
235 strcatW(wszFilePath, wszIndex);
237 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
238 if (hFile == INVALID_HANDLE_VALUE)
240 /* Maybe the directory wasn't there? Try to create it */
241 if (CreateDirectoryW(pContainer->path, 0))
242 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
244 if (hFile == INVALID_HANDLE_VALUE)
246 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
247 ReleaseMutex(pContainer->hMutex);
248 return GetLastError();
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 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
424 URLCache_PathToObjectName(wszFilePath, '_');
425 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
426 if (!pContainer->hMapping)
427 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
428 CloseHandle(hFile);
429 if (!pContainer->hMapping)
431 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
432 ReleaseMutex(pContainer->hMutex);
433 return GetLastError();
436 ReleaseMutex(pContainer->hMutex);
438 return ERROR_SUCCESS;
441 /***********************************************************************
442 * URLCacheContainer_CloseIndex (Internal)
444 * Closes the index
446 * RETURNS
447 * nothing
450 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
452 CloseHandle(pContainer->hMapping);
453 pContainer->hMapping = NULL;
456 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
458 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
459 int cache_prefix_len = strlenW(cache_prefix);
461 if (!pContainer)
463 return FALSE;
466 pContainer->hMapping = NULL;
467 pContainer->file_size = 0;
469 pContainer->path = heap_strdupW(path);
470 if (!pContainer->path)
472 heap_free(pContainer);
473 return FALSE;
476 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
477 if (!pContainer->cache_prefix)
479 heap_free(pContainer->path);
480 heap_free(pContainer);
481 return FALSE;
484 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
486 CharLowerW(mutex_name);
487 URLCache_PathToObjectName(mutex_name, '!');
489 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
491 ERR("couldn't create mutex (error is %d)\n", GetLastError());
492 heap_free(pContainer->path);
493 heap_free(pContainer);
494 return FALSE;
497 list_add_head(&UrlContainers, &pContainer->entry);
499 return TRUE;
502 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
504 list_remove(&pContainer->entry);
506 URLCacheContainer_CloseIndex(pContainer);
507 CloseHandle(pContainer->hMutex);
508 heap_free(pContainer->path);
509 heap_free(pContainer->cache_prefix);
510 heap_free(pContainer);
513 void URLCacheContainers_CreateDefaults(void)
515 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
516 static const WCHAR UrlPrefix[] = {0};
517 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
518 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
519 static const WCHAR CookieSuffix[] = {0};
520 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
521 static const struct
523 int nFolder; /* CSIDL_* constant */
524 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
525 const WCHAR * cache_prefix; /* prefix used to reference the container */
526 } DefaultContainerData[] =
528 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
529 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
530 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
532 DWORD i;
534 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
536 WCHAR wszCachePath[MAX_PATH];
537 WCHAR wszMutexName[MAX_PATH];
538 int path_len, suffix_len;
540 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
542 ERR("Couldn't get path for default container %u\n", i);
543 continue;
545 path_len = strlenW(wszCachePath);
546 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
548 if (path_len + suffix_len + 2 > MAX_PATH)
550 ERR("Path too long\n");
551 continue;
554 wszCachePath[path_len] = '\\';
555 wszCachePath[path_len+1] = 0;
557 strcpyW(wszMutexName, wszCachePath);
559 if (suffix_len)
561 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
562 wszCachePath[path_len + suffix_len + 1] = '\\';
563 wszCachePath[path_len + suffix_len + 2] = '\0';
566 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
570 void URLCacheContainers_DeleteAll(void)
572 while(!list_empty(&UrlContainers))
573 URLCacheContainer_DeleteContainer(
574 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
578 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
580 URLCACHECONTAINER * pContainer;
582 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
584 if(!lpwszUrl)
585 return ERROR_INVALID_PARAMETER;
587 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
589 int prefix_len = strlenW(pContainer->cache_prefix);
590 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
592 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
593 *ppContainer = pContainer;
594 return ERROR_SUCCESS;
597 ERR("no container found\n");
598 return ERROR_FILE_NOT_FOUND;
601 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
603 LPWSTR url = NULL;
604 DWORD ret;
606 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
607 return ERROR_OUTOFMEMORY;
609 ret = URLCacheContainers_FindContainerW(url, ppContainer);
610 heap_free(url);
611 return ret;
614 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
616 DWORD i = 0;
617 URLCACHECONTAINER * pContainer;
619 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
621 /* non-NULL search pattern only returns one container ever */
622 if (lpwszSearchPattern && dwIndex > 0)
623 return FALSE;
625 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
627 if (lpwszSearchPattern)
629 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
631 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
632 *ppContainer = pContainer;
633 return TRUE;
636 else
638 if (i == dwIndex)
640 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
641 *ppContainer = pContainer;
642 return TRUE;
645 i++;
647 return FALSE;
650 /***********************************************************************
651 * URLCacheContainer_LockIndex (Internal)
653 * Locks the index for system-wide exclusive access.
655 * RETURNS
656 * Cache file header if successful
657 * NULL if failed and calls SetLastError.
659 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
661 BYTE index;
662 LPVOID pIndexData;
663 URLCACHE_HEADER * pHeader;
664 DWORD error;
666 /* acquire mutex */
667 WaitForSingleObject(pContainer->hMutex, INFINITE);
669 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
671 if (!pIndexData)
673 ReleaseMutex(pContainer->hMutex);
674 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
675 return NULL;
677 pHeader = (URLCACHE_HEADER *)pIndexData;
679 /* file has grown - we need to remap to prevent us getting
680 * access violations when we try and access beyond the end
681 * of the memory mapped file */
682 if (pHeader->dwFileSize != pContainer->file_size)
684 UnmapViewOfFile( pHeader );
685 URLCacheContainer_CloseIndex(pContainer);
686 error = URLCacheContainer_OpenIndex(pContainer);
687 if (error != ERROR_SUCCESS)
689 ReleaseMutex(pContainer->hMutex);
690 SetLastError(error);
691 return NULL;
693 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
695 if (!pIndexData)
697 ReleaseMutex(pContainer->hMutex);
698 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
699 return NULL;
701 pHeader = (URLCACHE_HEADER *)pIndexData;
704 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
706 for (index = 0; index < pHeader->DirectoryCount; index++)
708 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
711 return pHeader;
714 /***********************************************************************
715 * URLCacheContainer_UnlockIndex (Internal)
718 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
720 /* release mutex */
721 ReleaseMutex(pContainer->hMutex);
722 return UnmapViewOfFile(pHeader);
726 #ifndef CHAR_BIT
727 #define CHAR_BIT (8 * sizeof(CHAR))
728 #endif
730 /***********************************************************************
731 * URLCache_Allocation_BlockIsFree (Internal)
733 * Is the specified block number free?
735 * RETURNS
736 * zero if free
737 * non-zero otherwise
740 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
742 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
743 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
746 /***********************************************************************
747 * URLCache_Allocation_BlockFree (Internal)
749 * Marks the specified block as free
751 * RETURNS
752 * nothing
755 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
757 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
758 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
761 /***********************************************************************
762 * URLCache_Allocation_BlockAllocate (Internal)
764 * Marks the specified block as allocated
766 * RETURNS
767 * nothing
770 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
772 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
773 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
776 /***********************************************************************
777 * URLCache_FindFirstFreeEntry (Internal)
779 * Finds and allocates the first block of free space big enough and
780 * sets ppEntry to point to it.
782 * RETURNS
783 * TRUE if it had enough space
784 * FALSE if it couldn't find enough space
787 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
789 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
790 DWORD dwBlockNumber;
791 DWORD dwFreeCounter;
792 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
794 for (dwFreeCounter = 0;
795 dwFreeCounter < dwBlocksNeeded &&
796 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
797 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
798 dwFreeCounter++)
799 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
801 if (dwFreeCounter == dwBlocksNeeded)
803 DWORD index;
804 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
805 for (index = 0; index < dwBlocksNeeded; index++)
806 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
807 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
808 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
809 return TRUE;
812 FIXME("Grow file\n");
813 return FALSE;
816 /***********************************************************************
817 * URLCache_DeleteEntry (Internal)
819 * Deletes the specified entry and frees the space allocated to it
821 * RETURNS
822 * TRUE if it succeeded
823 * FALSE if it failed
826 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
828 DWORD dwStartBlock;
829 DWORD dwBlock;
830 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
832 /* update allocation table */
833 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
834 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
835 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
837 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
838 return TRUE;
841 /***********************************************************************
842 * URLCache_LocalFileNameToPathW (Internal)
844 * Copies the full path to the specified buffer given the local file
845 * name and the index of the directory it is in. Always sets value in
846 * lpBufferSize to the required buffer size (in bytes).
848 * RETURNS
849 * TRUE if the buffer was big enough
850 * FALSE if the buffer was too small
853 static BOOL URLCache_LocalFileNameToPathW(
854 const URLCACHECONTAINER * pContainer,
855 LPCURLCACHE_HEADER pHeader,
856 LPCSTR szLocalFileName,
857 BYTE Directory,
858 LPWSTR wszPath,
859 LPLONG lpBufferSize)
861 LONG nRequired;
862 int path_len = strlenW(pContainer->path);
863 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
864 if (Directory >= pHeader->DirectoryCount)
866 *lpBufferSize = 0;
867 return FALSE;
870 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
871 if (nRequired <= *lpBufferSize)
873 int dir_len;
875 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
876 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
877 wszPath[dir_len + path_len] = '\\';
878 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
879 *lpBufferSize = nRequired;
880 return TRUE;
882 *lpBufferSize = nRequired;
883 return FALSE;
886 /***********************************************************************
887 * URLCache_LocalFileNameToPathA (Internal)
889 * Copies the full path to the specified buffer given the local file
890 * name and the index of the directory it is in. Always sets value in
891 * lpBufferSize to the required buffer size.
893 * RETURNS
894 * TRUE if the buffer was big enough
895 * FALSE if the buffer was too small
898 static BOOL URLCache_LocalFileNameToPathA(
899 const URLCACHECONTAINER * pContainer,
900 LPCURLCACHE_HEADER pHeader,
901 LPCSTR szLocalFileName,
902 BYTE Directory,
903 LPSTR szPath,
904 LPLONG lpBufferSize)
906 LONG nRequired;
907 int path_len, file_name_len, dir_len;
909 if (Directory >= pHeader->DirectoryCount)
911 *lpBufferSize = 0;
912 return FALSE;
915 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
916 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
917 dir_len = DIR_LENGTH;
919 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
920 if (nRequired < *lpBufferSize)
922 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
923 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
924 szPath[path_len + dir_len] = '\\';
925 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
926 *lpBufferSize = nRequired;
927 return TRUE;
929 *lpBufferSize = nRequired;
930 return FALSE;
933 /* Just like DosDateTimeToFileTime, except that it also maps the special
934 * case of a DOS date/time of (0,0) to a filetime of (0,0).
936 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
937 FILETIME *ft)
939 if (!fatdate && !fattime)
940 ft->dwLowDateTime = ft->dwHighDateTime = 0;
941 else
942 DosDateTimeToFileTime(fatdate, fattime, ft);
945 /***********************************************************************
946 * URLCache_CopyEntry (Internal)
948 * Copies an entry from the cache index file to the Win32 structure
950 * RETURNS
951 * ERROR_SUCCESS if the buffer was big enough
952 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
955 static DWORD URLCache_CopyEntry(
956 URLCACHECONTAINER * pContainer,
957 LPCURLCACHE_HEADER pHeader,
958 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
959 LPDWORD lpdwBufferSize,
960 const URL_CACHEFILE_ENTRY * pUrlEntry,
961 BOOL bUnicode)
963 int lenUrl;
964 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
966 if (*lpdwBufferSize >= dwRequiredSize)
968 lpCacheEntryInfo->lpHeaderInfo = NULL;
969 lpCacheEntryInfo->lpszFileExtension = NULL;
970 lpCacheEntryInfo->lpszLocalFileName = NULL;
971 lpCacheEntryInfo->lpszSourceUrlName = NULL;
972 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
973 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
974 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
975 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
976 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
977 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
978 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
979 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
980 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
981 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
982 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
983 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
984 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
985 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
988 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
989 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
990 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
991 if (bUnicode)
992 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
993 else
994 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
995 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
997 /* FIXME: is source url optional? */
998 if (*lpdwBufferSize >= dwRequiredSize)
1000 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1002 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1003 if (bUnicode)
1004 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1005 else
1006 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1009 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1010 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1011 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1013 if (pUrlEntry->dwOffsetLocalName)
1015 LONG nLocalFilePathSize;
1016 LPSTR lpszLocalFileName;
1017 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1018 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1019 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1020 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1022 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1024 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1026 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1027 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1028 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1030 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1032 if (*lpdwBufferSize >= dwRequiredSize)
1034 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1035 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1036 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1038 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1039 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1040 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1042 if (pUrlEntry->dwOffsetFileExtension)
1044 int lenExtension;
1046 if (bUnicode)
1047 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1048 else
1049 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1050 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1052 if (*lpdwBufferSize >= dwRequiredSize)
1054 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1055 if (bUnicode)
1056 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1057 else
1058 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1061 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1062 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1063 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1066 if (dwRequiredSize > *lpdwBufferSize)
1068 *lpdwBufferSize = dwRequiredSize;
1069 return ERROR_INSUFFICIENT_BUFFER;
1071 *lpdwBufferSize = dwRequiredSize;
1072 return ERROR_SUCCESS;
1075 /* Just like FileTimeToDosDateTime, except that it also maps the special
1076 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1078 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1079 WORD *fattime)
1081 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1082 *fatdate = *fattime = 0;
1083 else
1084 FileTimeToDosDateTime(ft, fatdate, fattime);
1087 /***********************************************************************
1088 * URLCache_SetEntryInfo (Internal)
1090 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1091 * according to the flags set by dwFieldControl.
1093 * RETURNS
1094 * ERROR_SUCCESS if the buffer was big enough
1095 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1098 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1100 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1101 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1102 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1103 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1104 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1105 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1106 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1107 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1108 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1109 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1110 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1111 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1112 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1113 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1114 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1115 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1117 return ERROR_SUCCESS;
1120 /***********************************************************************
1121 * URLCache_HashKey (Internal)
1123 * Returns the hash key for a given string
1125 * RETURNS
1126 * hash key for the string
1129 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1131 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1132 * but the algorithm and result are not the same!
1134 static const unsigned char lookupTable[256] =
1136 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1137 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1138 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1139 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1140 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1141 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1142 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1143 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1144 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1145 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1146 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1147 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1148 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1149 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1150 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1151 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1152 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1153 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1154 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1155 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1156 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1157 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1158 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1159 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1160 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1161 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1162 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1163 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1164 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1165 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1166 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1167 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1169 BYTE key[4];
1170 DWORD i;
1172 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1173 key[i] = lookupTable[i];
1175 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1177 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1178 key[i] = lookupTable[*lpszKey ^ key[i]];
1181 return *(DWORD *)key;
1184 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1186 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1189 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1191 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1192 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1193 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1196 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1198 /* structure of hash table:
1199 * 448 entries divided into 64 blocks
1200 * each block therefore contains a chain of 7 key/offset pairs
1201 * how position in table is calculated:
1202 * 1. the url is hashed in helper function
1203 * 2. the key % 64 * 8 is the offset
1204 * 3. the key in the hash table is the hash key aligned to 64
1206 * note:
1207 * there can be multiple hash tables in the file and the offset to
1208 * the next one is stored in the header of the hash table
1210 DWORD key = URLCache_HashKey(lpszUrl);
1211 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1212 HASH_CACHEFILE_ENTRY * pHashEntry;
1213 DWORD dwHashTableNumber = 0;
1215 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1217 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1218 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1219 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1221 int i;
1222 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1224 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1225 continue;
1227 /* make sure that it is in fact a hash entry */
1228 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1230 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1231 continue;
1234 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1236 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1237 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1239 /* FIXME: we should make sure that this is the right element
1240 * before returning and claiming that it is. We can do this
1241 * by doing a simple compare between the URL we were given
1242 * and the URL stored in the entry. However, this assumes
1243 * we know the format of all the entries stored in the
1244 * hash table */
1245 *ppHashEntry = pHashElement;
1246 return TRUE;
1250 return FALSE;
1253 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1255 LPSTR urlA;
1256 BOOL ret;
1258 urlA = heap_strdupWtoA(lpszUrl);
1259 if (!urlA)
1261 SetLastError(ERROR_OUTOFMEMORY);
1262 return FALSE;
1265 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1266 heap_free(urlA);
1267 return ret;
1270 /***********************************************************************
1271 * URLCache_HashEntrySetUse (Internal)
1273 * Searches all the hash tables in the index for the given URL and
1274 * sets the use count (stored or'ed with key)
1276 * RETURNS
1277 * TRUE if the entry was found
1278 * FALSE if the entry could not be found
1281 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1283 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1284 return TRUE;
1287 /***********************************************************************
1288 * URLCache_DeleteEntryFromHash (Internal)
1290 * Searches all the hash tables in the index for the given URL and
1291 * then if found deletes the entry.
1293 * RETURNS
1294 * TRUE if the entry was found
1295 * FALSE if the entry could not be found
1298 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1300 pHashEntry->dwHashKey = HASHTABLE_FREE;
1301 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1302 return TRUE;
1305 /***********************************************************************
1306 * URLCache_AddEntryToHash (Internal)
1308 * Searches all the hash tables for a free slot based on the offset
1309 * generated from the hash key. If a free slot is found, the offset and
1310 * key are entered into the hash table.
1312 * RETURNS
1313 * ERROR_SUCCESS if the entry was added
1314 * Any other Win32 error code if the entry could not be added
1317 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1319 /* see URLCache_FindEntryInHash for structure of hash tables */
1321 DWORD key = URLCache_HashKey(lpszUrl);
1322 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1323 HASH_CACHEFILE_ENTRY * pHashEntry;
1324 DWORD dwHashTableNumber = 0;
1325 DWORD error;
1327 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1329 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1330 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1331 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1333 int i;
1334 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1336 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1337 break;
1339 /* make sure that it is in fact a hash entry */
1340 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1342 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1343 break;
1346 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1348 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1349 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1351 pHashElement->dwHashKey = key;
1352 pHashElement->dwOffsetEntry = dwOffsetEntry;
1353 return ERROR_SUCCESS;
1357 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1358 if (error != ERROR_SUCCESS)
1359 return error;
1361 pHashEntry->HashTable[offset].dwHashKey = key;
1362 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1363 return ERROR_SUCCESS;
1366 /***********************************************************************
1367 * URLCache_CreateHashTable (Internal)
1369 * Creates a new hash table in free space and adds it to the chain of existing
1370 * hash tables.
1372 * RETURNS
1373 * ERROR_SUCCESS if the hash table was created
1374 * ERROR_DISK_FULL if the hash table could not be created
1377 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1379 DWORD dwOffset;
1380 int i;
1382 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1384 FIXME("no free space for hash table\n");
1385 return ERROR_DISK_FULL;
1388 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1390 if (pPrevHash)
1391 pPrevHash->dwAddressNext = dwOffset;
1392 else
1393 pHeader->dwOffsetFirstHashTable = dwOffset;
1394 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1395 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1396 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1397 for (i = 0; i < HASHTABLE_SIZE; i++)
1399 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1400 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1402 return ERROR_SUCCESS;
1405 /***********************************************************************
1406 * URLCache_EnumHashTables (Internal)
1408 * Enumerates the hash tables in a container.
1410 * RETURNS
1411 * TRUE if an entry was found
1412 * FALSE if there are no more tables to enumerate.
1415 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1417 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1418 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1419 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1421 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1422 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1423 continue;
1424 /* make sure that it is in fact a hash entry */
1425 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1427 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1428 (*pdwHashTableNumber)++;
1429 continue;
1432 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1433 return TRUE;
1435 return FALSE;
1438 /***********************************************************************
1439 * URLCache_EnumHashTableEntries (Internal)
1441 * Enumerates entries in a hash table and returns the next non-free entry.
1443 * RETURNS
1444 * TRUE if an entry was found
1445 * FALSE if the hash table is empty or there are no more entries to
1446 * enumerate.
1449 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1450 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1452 for (; *index < HASHTABLE_SIZE ; (*index)++)
1454 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1455 continue;
1457 *ppHashEntry = &pHashEntry->HashTable[*index];
1458 TRACE("entry found %d\n", *index);
1459 return TRUE;
1461 TRACE("no more entries (%d)\n", *index);
1462 return FALSE;
1465 /***********************************************************************
1466 * URLCache_DeleteCacheDirectory (Internal)
1468 * Erase a directory containing an URL cache.
1470 * RETURNS
1471 * TRUE success, FALSE failure/aborted.
1474 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1476 DWORD path_len;
1477 WCHAR path[MAX_PATH + 1];
1478 SHFILEOPSTRUCTW shfos;
1479 int ret;
1481 path_len = strlenW(lpszPath);
1482 if (path_len >= MAX_PATH)
1483 return FALSE;
1484 strcpyW(path, lpszPath);
1485 path[path_len + 1] = 0; /* double-NUL-terminate path */
1487 shfos.hwnd = NULL;
1488 shfos.wFunc = FO_DELETE;
1489 shfos.pFrom = path;
1490 shfos.pTo = NULL;
1491 shfos.fFlags = 0;
1492 shfos.fAnyOperationsAborted = FALSE;
1493 ret = SHFileOperationW(&shfos);
1494 if (ret)
1495 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1496 return !(ret || shfos.fAnyOperationsAborted);
1499 /***********************************************************************
1500 * FreeUrlCacheSpaceW (WININET.@)
1502 * Frees up some cache.
1504 * PARAMETERS
1505 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1506 * dwSize [I] How much space to free up.
1507 * dwSizeType [I] How to interpret dwSize.
1509 * RETURNS
1510 * TRUE success. FALSE failure.
1512 * IMPLEMENTATION
1513 * This implementation just retrieves the path of the cache directory, and
1514 * deletes its contents from the filesystem. The correct approach would
1515 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1517 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1519 URLCACHECONTAINER * pContainer;
1521 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1523 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1524 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1525 return FALSE;
1528 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1530 /* The URL cache has prefix L"" (unlike Cookies and History) */
1531 if (pContainer->cache_prefix[0] == 0)
1533 BOOL ret_del;
1534 DWORD ret_open;
1535 WaitForSingleObject(pContainer->hMutex, INFINITE);
1537 /* unlock, delete, recreate and lock cache */
1538 URLCacheContainer_CloseIndex(pContainer);
1539 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1540 ret_open = URLCacheContainer_OpenIndex(pContainer);
1542 ReleaseMutex(pContainer->hMutex);
1543 return ret_del && (ret_open == ERROR_SUCCESS);
1546 return FALSE;
1549 /***********************************************************************
1550 * FreeUrlCacheSpaceA (WININET.@)
1552 * See FreeUrlCacheSpaceW.
1554 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1556 BOOL ret = FALSE;
1557 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1558 if (lpszCachePath == NULL || path != NULL)
1559 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1560 heap_free(path);
1561 return ret;
1564 /***********************************************************************
1565 * GetUrlCacheEntryInfoExA (WININET.@)
1568 BOOL WINAPI GetUrlCacheEntryInfoExA(
1569 LPCSTR lpszUrl,
1570 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1571 LPDWORD lpdwCacheEntryInfoBufSize,
1572 LPSTR lpszReserved,
1573 LPDWORD lpdwReserved,
1574 LPVOID lpReserved,
1575 DWORD dwFlags)
1577 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1578 debugstr_a(lpszUrl),
1579 lpCacheEntryInfo,
1580 lpdwCacheEntryInfoBufSize,
1581 lpszReserved,
1582 lpdwReserved,
1583 lpReserved,
1584 dwFlags);
1586 if ((lpszReserved != NULL) ||
1587 (lpdwReserved != NULL) ||
1588 (lpReserved != NULL))
1590 ERR("Reserved value was not 0\n");
1591 SetLastError(ERROR_INVALID_PARAMETER);
1592 return FALSE;
1594 if (dwFlags != 0)
1596 FIXME("Undocumented flag(s): %x\n", dwFlags);
1597 SetLastError(ERROR_FILE_NOT_FOUND);
1598 return FALSE;
1600 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1603 /***********************************************************************
1604 * GetUrlCacheEntryInfoA (WININET.@)
1607 BOOL WINAPI GetUrlCacheEntryInfoA(
1608 IN LPCSTR lpszUrlName,
1609 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1610 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1613 LPURLCACHE_HEADER pHeader;
1614 struct _HASH_ENTRY * pHashEntry;
1615 const CACHEFILE_ENTRY * pEntry;
1616 const URL_CACHEFILE_ENTRY * pUrlEntry;
1617 URLCACHECONTAINER * pContainer;
1618 DWORD error;
1620 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1622 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1623 if (error != ERROR_SUCCESS)
1625 SetLastError(error);
1626 return FALSE;
1629 error = URLCacheContainer_OpenIndex(pContainer);
1630 if (error != ERROR_SUCCESS)
1632 SetLastError(error);
1633 return FALSE;
1636 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1637 return FALSE;
1639 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1641 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1642 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1643 SetLastError(ERROR_FILE_NOT_FOUND);
1644 return FALSE;
1647 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1648 if (pEntry->dwSignature != URL_SIGNATURE)
1650 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1651 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1652 SetLastError(ERROR_FILE_NOT_FOUND);
1653 return FALSE;
1656 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1657 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1658 if (pUrlEntry->dwOffsetHeaderInfo)
1659 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1661 if (lpdwCacheEntryInfoBufferSize)
1663 if (!lpCacheEntryInfo)
1664 *lpdwCacheEntryInfoBufferSize = 0;
1666 error = URLCache_CopyEntry(
1667 pContainer,
1668 pHeader,
1669 lpCacheEntryInfo,
1670 lpdwCacheEntryInfoBufferSize,
1671 pUrlEntry,
1672 FALSE /* ANSI */);
1673 if (error != ERROR_SUCCESS)
1675 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1676 SetLastError(error);
1677 return FALSE;
1679 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1682 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1684 return TRUE;
1687 /***********************************************************************
1688 * GetUrlCacheEntryInfoW (WININET.@)
1691 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1692 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1693 LPDWORD lpdwCacheEntryInfoBufferSize)
1695 LPURLCACHE_HEADER pHeader;
1696 struct _HASH_ENTRY * pHashEntry;
1697 const CACHEFILE_ENTRY * pEntry;
1698 const URL_CACHEFILE_ENTRY * pUrlEntry;
1699 URLCACHECONTAINER * pContainer;
1700 DWORD error;
1702 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1704 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1705 if (error != ERROR_SUCCESS)
1707 SetLastError(error);
1708 return FALSE;
1711 error = URLCacheContainer_OpenIndex(pContainer);
1712 if (error != ERROR_SUCCESS)
1714 SetLastError(error);
1715 return FALSE;
1718 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1719 return FALSE;
1721 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1723 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1724 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1725 SetLastError(ERROR_FILE_NOT_FOUND);
1726 return FALSE;
1729 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1730 if (pEntry->dwSignature != URL_SIGNATURE)
1732 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1733 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1734 SetLastError(ERROR_FILE_NOT_FOUND);
1735 return FALSE;
1738 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1739 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1740 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1742 if (lpdwCacheEntryInfoBufferSize)
1744 if (!lpCacheEntryInfo)
1745 *lpdwCacheEntryInfoBufferSize = 0;
1747 error = URLCache_CopyEntry(
1748 pContainer,
1749 pHeader,
1750 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1751 lpdwCacheEntryInfoBufferSize,
1752 pUrlEntry,
1753 TRUE /* UNICODE */);
1754 if (error != ERROR_SUCCESS)
1756 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1757 SetLastError(error);
1758 return FALSE;
1760 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1763 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1765 return TRUE;
1768 /***********************************************************************
1769 * GetUrlCacheEntryInfoExW (WININET.@)
1772 BOOL WINAPI GetUrlCacheEntryInfoExW(
1773 LPCWSTR lpszUrl,
1774 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1775 LPDWORD lpdwCacheEntryInfoBufSize,
1776 LPWSTR lpszReserved,
1777 LPDWORD lpdwReserved,
1778 LPVOID lpReserved,
1779 DWORD dwFlags)
1781 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1782 debugstr_w(lpszUrl),
1783 lpCacheEntryInfo,
1784 lpdwCacheEntryInfoBufSize,
1785 lpszReserved,
1786 lpdwReserved,
1787 lpReserved,
1788 dwFlags);
1790 if ((lpszReserved != NULL) ||
1791 (lpdwReserved != NULL) ||
1792 (lpReserved != NULL))
1794 ERR("Reserved value was not 0\n");
1795 SetLastError(ERROR_INVALID_PARAMETER);
1796 return FALSE;
1798 if (dwFlags != 0)
1800 FIXME("Undocumented flag(s): %x\n", dwFlags);
1801 SetLastError(ERROR_FILE_NOT_FOUND);
1802 return FALSE;
1804 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1807 /***********************************************************************
1808 * SetUrlCacheEntryInfoA (WININET.@)
1810 BOOL WINAPI SetUrlCacheEntryInfoA(
1811 LPCSTR lpszUrlName,
1812 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1813 DWORD dwFieldControl)
1815 LPURLCACHE_HEADER pHeader;
1816 struct _HASH_ENTRY * pHashEntry;
1817 CACHEFILE_ENTRY * pEntry;
1818 URLCACHECONTAINER * pContainer;
1819 DWORD error;
1821 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1823 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1824 if (error != ERROR_SUCCESS)
1826 SetLastError(error);
1827 return FALSE;
1830 error = URLCacheContainer_OpenIndex(pContainer);
1831 if (error != ERROR_SUCCESS)
1833 SetLastError(error);
1834 return FALSE;
1837 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1838 return FALSE;
1840 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1842 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1843 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1844 SetLastError(ERROR_FILE_NOT_FOUND);
1845 return FALSE;
1848 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1849 if (pEntry->dwSignature != URL_SIGNATURE)
1851 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1852 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1853 SetLastError(ERROR_FILE_NOT_FOUND);
1854 return FALSE;
1857 URLCache_SetEntryInfo(
1858 (URL_CACHEFILE_ENTRY *)pEntry,
1859 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1860 dwFieldControl);
1862 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1864 return TRUE;
1867 /***********************************************************************
1868 * SetUrlCacheEntryInfoW (WININET.@)
1870 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1872 LPURLCACHE_HEADER pHeader;
1873 struct _HASH_ENTRY * pHashEntry;
1874 CACHEFILE_ENTRY * pEntry;
1875 URLCACHECONTAINER * pContainer;
1876 DWORD error;
1878 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1880 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1881 if (error != ERROR_SUCCESS)
1883 SetLastError(error);
1884 return FALSE;
1887 error = URLCacheContainer_OpenIndex(pContainer);
1888 if (error != ERROR_SUCCESS)
1890 SetLastError(error);
1891 return FALSE;
1894 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1895 return FALSE;
1897 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1899 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1900 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1901 SetLastError(ERROR_FILE_NOT_FOUND);
1902 return FALSE;
1905 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1906 if (pEntry->dwSignature != URL_SIGNATURE)
1908 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1909 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1910 SetLastError(ERROR_FILE_NOT_FOUND);
1911 return FALSE;
1914 URLCache_SetEntryInfo(
1915 (URL_CACHEFILE_ENTRY *)pEntry,
1916 lpCacheEntryInfo,
1917 dwFieldControl);
1919 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1921 return TRUE;
1924 /***********************************************************************
1925 * RetrieveUrlCacheEntryFileA (WININET.@)
1928 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1929 IN LPCSTR lpszUrlName,
1930 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1931 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1932 IN DWORD dwReserved
1935 LPURLCACHE_HEADER pHeader;
1936 struct _HASH_ENTRY * pHashEntry;
1937 CACHEFILE_ENTRY * pEntry;
1938 URL_CACHEFILE_ENTRY * pUrlEntry;
1939 URLCACHECONTAINER * pContainer;
1940 DWORD error;
1942 TRACE("(%s, %p, %p, 0x%08x)\n",
1943 debugstr_a(lpszUrlName),
1944 lpCacheEntryInfo,
1945 lpdwCacheEntryInfoBufferSize,
1946 dwReserved);
1948 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1949 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1951 SetLastError(ERROR_INVALID_PARAMETER);
1952 return FALSE;
1955 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1956 if (error != ERROR_SUCCESS)
1958 SetLastError(error);
1959 return FALSE;
1962 error = URLCacheContainer_OpenIndex(pContainer);
1963 if (error != ERROR_SUCCESS)
1965 SetLastError(error);
1966 return FALSE;
1969 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1970 return FALSE;
1972 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1974 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1975 TRACE("entry %s not found!\n", lpszUrlName);
1976 SetLastError(ERROR_FILE_NOT_FOUND);
1977 return FALSE;
1980 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1981 if (pEntry->dwSignature != URL_SIGNATURE)
1983 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1984 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1985 SetLastError(ERROR_FILE_NOT_FOUND);
1986 return FALSE;
1989 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1990 if (!pUrlEntry->dwOffsetLocalName)
1992 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1993 SetLastError(ERROR_INVALID_DATA);
1994 return FALSE;
1997 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1998 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2000 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2001 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2002 FALSE);
2003 if (error != ERROR_SUCCESS)
2005 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2006 SetLastError(error);
2007 return FALSE;
2009 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2011 pUrlEntry->dwHitRate++;
2012 pUrlEntry->dwUseCount++;
2013 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2014 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2016 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2018 return TRUE;
2021 /***********************************************************************
2022 * RetrieveUrlCacheEntryFileW (WININET.@)
2025 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2026 IN LPCWSTR lpszUrlName,
2027 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2028 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2029 IN DWORD dwReserved
2032 LPURLCACHE_HEADER pHeader;
2033 struct _HASH_ENTRY * pHashEntry;
2034 CACHEFILE_ENTRY * pEntry;
2035 URL_CACHEFILE_ENTRY * pUrlEntry;
2036 URLCACHECONTAINER * pContainer;
2037 DWORD error;
2039 TRACE("(%s, %p, %p, 0x%08x)\n",
2040 debugstr_w(lpszUrlName),
2041 lpCacheEntryInfo,
2042 lpdwCacheEntryInfoBufferSize,
2043 dwReserved);
2045 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2046 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2048 SetLastError(ERROR_INVALID_PARAMETER);
2049 return FALSE;
2052 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2053 if (error != ERROR_SUCCESS)
2055 SetLastError(error);
2056 return FALSE;
2059 error = URLCacheContainer_OpenIndex(pContainer);
2060 if (error != ERROR_SUCCESS)
2062 SetLastError(error);
2063 return FALSE;
2066 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2067 return FALSE;
2069 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2071 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2072 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2073 SetLastError(ERROR_FILE_NOT_FOUND);
2074 return FALSE;
2077 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2078 if (pEntry->dwSignature != URL_SIGNATURE)
2080 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2081 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2082 SetLastError(ERROR_FILE_NOT_FOUND);
2083 return FALSE;
2086 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2087 if (!pUrlEntry->dwOffsetLocalName)
2089 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2090 SetLastError(ERROR_INVALID_DATA);
2091 return FALSE;
2094 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2095 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2097 error = URLCache_CopyEntry(
2098 pContainer,
2099 pHeader,
2100 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2101 lpdwCacheEntryInfoBufferSize,
2102 pUrlEntry,
2103 TRUE /* UNICODE */);
2104 if (error != ERROR_SUCCESS)
2106 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2107 SetLastError(error);
2108 return FALSE;
2110 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2112 pUrlEntry->dwHitRate++;
2113 pUrlEntry->dwUseCount++;
2114 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2115 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2117 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2119 return TRUE;
2122 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2123 struct _HASH_ENTRY *pHashEntry)
2125 CACHEFILE_ENTRY * pEntry;
2126 URL_CACHEFILE_ENTRY * pUrlEntry;
2128 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2129 if (pEntry->dwSignature != URL_SIGNATURE)
2131 FIXME("Trying to delete entry of unknown format %s\n",
2132 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2133 SetLastError(ERROR_FILE_NOT_FOUND);
2134 return FALSE;
2136 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2137 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2139 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2140 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2142 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2144 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2145 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2146 else
2147 pHeader->ExemptUsage.QuadPart = 0;
2149 else
2151 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2152 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2153 else
2154 pHeader->CacheUsage.QuadPart = 0;
2157 URLCache_DeleteEntry(pHeader, pEntry);
2159 URLCache_DeleteEntryFromHash(pHashEntry);
2160 return TRUE;
2163 /***********************************************************************
2164 * UnlockUrlCacheEntryFileA (WININET.@)
2167 BOOL WINAPI UnlockUrlCacheEntryFileA(
2168 IN LPCSTR lpszUrlName,
2169 IN DWORD dwReserved
2172 LPURLCACHE_HEADER pHeader;
2173 struct _HASH_ENTRY * pHashEntry;
2174 CACHEFILE_ENTRY * pEntry;
2175 URL_CACHEFILE_ENTRY * pUrlEntry;
2176 URLCACHECONTAINER * pContainer;
2177 DWORD error;
2179 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2181 if (dwReserved)
2183 ERR("dwReserved != 0\n");
2184 SetLastError(ERROR_INVALID_PARAMETER);
2185 return FALSE;
2188 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2189 if (error != ERROR_SUCCESS)
2191 SetLastError(error);
2192 return FALSE;
2195 error = URLCacheContainer_OpenIndex(pContainer);
2196 if (error != ERROR_SUCCESS)
2198 SetLastError(error);
2199 return FALSE;
2202 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2203 return FALSE;
2205 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2207 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2208 TRACE("entry %s not found!\n", lpszUrlName);
2209 SetLastError(ERROR_FILE_NOT_FOUND);
2210 return FALSE;
2213 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2214 if (pEntry->dwSignature != URL_SIGNATURE)
2216 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2217 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2218 SetLastError(ERROR_FILE_NOT_FOUND);
2219 return FALSE;
2222 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2224 if (pUrlEntry->dwUseCount == 0)
2226 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2227 return FALSE;
2229 pUrlEntry->dwUseCount--;
2230 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2232 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2234 return TRUE;
2237 /***********************************************************************
2238 * UnlockUrlCacheEntryFileW (WININET.@)
2241 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2243 LPURLCACHE_HEADER pHeader;
2244 struct _HASH_ENTRY * pHashEntry;
2245 CACHEFILE_ENTRY * pEntry;
2246 URL_CACHEFILE_ENTRY * pUrlEntry;
2247 URLCACHECONTAINER * pContainer;
2248 DWORD error;
2250 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2252 if (dwReserved)
2254 ERR("dwReserved != 0\n");
2255 SetLastError(ERROR_INVALID_PARAMETER);
2256 return FALSE;
2259 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2260 if (error != ERROR_SUCCESS)
2262 SetLastError(error);
2263 return FALSE;
2266 error = URLCacheContainer_OpenIndex(pContainer);
2267 if (error != ERROR_SUCCESS)
2269 SetLastError(error);
2270 return FALSE;
2273 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2274 return FALSE;
2276 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2278 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2279 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2280 SetLastError(ERROR_FILE_NOT_FOUND);
2281 return FALSE;
2284 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2285 if (pEntry->dwSignature != URL_SIGNATURE)
2287 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2288 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2289 SetLastError(ERROR_FILE_NOT_FOUND);
2290 return FALSE;
2293 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2295 if (pUrlEntry->dwUseCount == 0)
2297 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2298 return FALSE;
2300 pUrlEntry->dwUseCount--;
2301 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2303 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2305 return TRUE;
2308 /***********************************************************************
2309 * CreateUrlCacheEntryA (WININET.@)
2312 BOOL WINAPI CreateUrlCacheEntryA(
2313 IN LPCSTR lpszUrlName,
2314 IN DWORD dwExpectedFileSize,
2315 IN LPCSTR lpszFileExtension,
2316 OUT LPSTR lpszFileName,
2317 IN DWORD dwReserved
2320 WCHAR *url_name;
2321 WCHAR *file_extension = NULL;
2322 WCHAR file_name[MAX_PATH];
2323 BOOL bSuccess = FALSE;
2324 DWORD dwError = 0;
2326 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2327 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2329 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2331 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2333 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2335 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2337 bSuccess = TRUE;
2339 else
2341 dwError = GetLastError();
2344 else
2346 dwError = GetLastError();
2348 heap_free(file_extension);
2350 else
2352 dwError = GetLastError();
2354 heap_free(url_name);
2355 if (!bSuccess) 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 heap_free(lpszUrlNameA);
2799 heap_free(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 heap_free(original_url);
2870 heap_free(file_extension);
2871 heap_free(local_file_name);
2872 heap_free(url_name);
2873 return bSuccess;
2876 /***********************************************************************
2877 * CommitUrlCacheEntryW (WININET.@)
2880 BOOL WINAPI CommitUrlCacheEntryW(
2881 IN LPCWSTR lpszUrlName,
2882 IN LPCWSTR lpszLocalFileName,
2883 IN FILETIME ExpireTime,
2884 IN FILETIME LastModifiedTime,
2885 IN DWORD CacheEntryType,
2886 IN LPWSTR lpHeaderInfo,
2887 IN DWORD dwHeaderSize,
2888 IN LPCWSTR lpszFileExtension,
2889 IN LPCWSTR lpszOriginalUrl
2892 DWORD dwError = 0;
2893 BOOL bSuccess = FALSE;
2894 DWORD len = 0;
2895 CHAR *header_info = NULL;
2897 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2898 debugstr_w(lpszUrlName),
2899 debugstr_w(lpszLocalFileName),
2900 CacheEntryType,
2901 lpHeaderInfo,
2902 dwHeaderSize,
2903 debugstr_w(lpszFileExtension),
2904 debugstr_w(lpszOriginalUrl));
2906 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2908 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2909 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2911 bSuccess = TRUE;
2913 else
2915 dwError = GetLastError();
2917 if (header_info)
2919 heap_free(header_info);
2920 if (!bSuccess)
2921 SetLastError(dwError);
2924 return bSuccess;
2927 /***********************************************************************
2928 * ReadUrlCacheEntryStream (WININET.@)
2931 BOOL WINAPI ReadUrlCacheEntryStream(
2932 IN HANDLE hUrlCacheStream,
2933 IN DWORD dwLocation,
2934 IN OUT LPVOID lpBuffer,
2935 IN OUT LPDWORD lpdwLen,
2936 IN DWORD dwReserved
2939 /* Get handle to file from 'stream' */
2940 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2942 if (dwReserved != 0)
2944 ERR("dwReserved != 0\n");
2945 SetLastError(ERROR_INVALID_PARAMETER);
2946 return FALSE;
2949 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2951 SetLastError(ERROR_INVALID_HANDLE);
2952 return FALSE;
2955 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2956 return FALSE;
2957 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2960 /***********************************************************************
2961 * RetrieveUrlCacheEntryStreamA (WININET.@)
2964 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2965 IN LPCSTR lpszUrlName,
2966 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2967 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2968 IN BOOL fRandomRead,
2969 IN DWORD dwReserved
2972 /* NOTE: this is not the same as the way that the native
2973 * version allocates 'stream' handles. I did it this way
2974 * as it is much easier and no applications should depend
2975 * on this behaviour. (Native version appears to allocate
2976 * indices into a table)
2978 STREAM_HANDLE * pStream;
2979 HANDLE hFile;
2981 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2982 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2984 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2985 lpCacheEntryInfo,
2986 lpdwCacheEntryInfoBufferSize,
2987 dwReserved))
2989 return NULL;
2992 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2993 GENERIC_READ,
2994 FILE_SHARE_READ,
2995 NULL,
2996 OPEN_EXISTING,
2997 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2998 NULL);
2999 if (hFile == INVALID_HANDLE_VALUE)
3000 return FALSE;
3002 /* allocate handle storage space */
3003 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3004 if (!pStream)
3006 CloseHandle(hFile);
3007 SetLastError(ERROR_OUTOFMEMORY);
3008 return FALSE;
3011 pStream->hFile = hFile;
3012 strcpy(pStream->lpszUrl, lpszUrlName);
3013 return pStream;
3016 /***********************************************************************
3017 * RetrieveUrlCacheEntryStreamW (WININET.@)
3020 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3021 IN LPCWSTR lpszUrlName,
3022 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3023 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3024 IN BOOL fRandomRead,
3025 IN DWORD dwReserved
3028 DWORD size;
3029 int url_len;
3030 /* NOTE: this is not the same as the way that the native
3031 * version allocates 'stream' handles. I did it this way
3032 * as it is much easier and no applications should depend
3033 * on this behaviour. (Native version appears to allocate
3034 * indices into a table)
3036 STREAM_HANDLE * pStream;
3037 HANDLE hFile;
3039 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3040 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3042 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3043 lpCacheEntryInfo,
3044 lpdwCacheEntryInfoBufferSize,
3045 dwReserved))
3047 return NULL;
3050 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3051 GENERIC_READ,
3052 FILE_SHARE_READ,
3053 NULL,
3054 OPEN_EXISTING,
3055 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3056 NULL);
3057 if (hFile == INVALID_HANDLE_VALUE)
3058 return FALSE;
3060 /* allocate handle storage space */
3061 size = sizeof(STREAM_HANDLE);
3062 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3063 size += url_len;
3064 pStream = heap_alloc(size);
3065 if (!pStream)
3067 CloseHandle(hFile);
3068 SetLastError(ERROR_OUTOFMEMORY);
3069 return FALSE;
3072 pStream->hFile = hFile;
3073 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3074 return pStream;
3077 /***********************************************************************
3078 * UnlockUrlCacheEntryStream (WININET.@)
3081 BOOL WINAPI UnlockUrlCacheEntryStream(
3082 IN HANDLE hUrlCacheStream,
3083 IN DWORD dwReserved
3086 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3088 if (dwReserved != 0)
3090 ERR("dwReserved != 0\n");
3091 SetLastError(ERROR_INVALID_PARAMETER);
3092 return FALSE;
3095 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3097 SetLastError(ERROR_INVALID_HANDLE);
3098 return FALSE;
3101 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3102 return FALSE;
3104 CloseHandle(pStream->hFile);
3105 heap_free(pStream);
3106 return TRUE;
3110 /***********************************************************************
3111 * DeleteUrlCacheEntryA (WININET.@)
3114 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3116 URLCACHECONTAINER * pContainer;
3117 LPURLCACHE_HEADER pHeader;
3118 struct _HASH_ENTRY * pHashEntry;
3119 DWORD error;
3120 BOOL ret;
3122 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3124 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3125 if (error != ERROR_SUCCESS)
3127 SetLastError(error);
3128 return FALSE;
3131 error = URLCacheContainer_OpenIndex(pContainer);
3132 if (error != ERROR_SUCCESS)
3134 SetLastError(error);
3135 return FALSE;
3138 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3139 return FALSE;
3141 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3143 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3144 TRACE("entry %s not found!\n", lpszUrlName);
3145 SetLastError(ERROR_FILE_NOT_FOUND);
3146 return FALSE;
3149 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3151 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3153 return ret;
3156 /***********************************************************************
3157 * DeleteUrlCacheEntryW (WININET.@)
3160 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3162 URLCACHECONTAINER * pContainer;
3163 LPURLCACHE_HEADER pHeader;
3164 struct _HASH_ENTRY * pHashEntry;
3165 LPSTR urlA;
3166 DWORD error;
3167 BOOL ret;
3169 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3171 urlA = heap_strdupWtoA(lpszUrlName);
3172 if (!urlA)
3174 SetLastError(ERROR_OUTOFMEMORY);
3175 return FALSE;
3178 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3179 if (error != ERROR_SUCCESS)
3181 heap_free(urlA);
3182 SetLastError(error);
3183 return FALSE;
3186 error = URLCacheContainer_OpenIndex(pContainer);
3187 if (error != ERROR_SUCCESS)
3189 heap_free(urlA);
3190 SetLastError(error);
3191 return FALSE;
3194 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3196 heap_free(urlA);
3197 return FALSE;
3200 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3202 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3203 TRACE("entry %s not found!\n", debugstr_a(urlA));
3204 heap_free(urlA);
3205 SetLastError(ERROR_FILE_NOT_FOUND);
3206 return FALSE;
3209 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3211 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3212 heap_free(urlA);
3213 return ret;
3216 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3218 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3219 return TRUE;
3222 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3224 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3225 return TRUE;
3228 /***********************************************************************
3229 * CreateCacheContainerA (WININET.@)
3231 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3232 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3234 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3235 d1, d2, d3, d4, d5, d6, d7, d8);
3236 return TRUE;
3239 /***********************************************************************
3240 * CreateCacheContainerW (WININET.@)
3242 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3243 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3245 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3246 d1, d2, d3, d4, d5, d6, d7, d8);
3247 return TRUE;
3250 /***********************************************************************
3251 * FindFirstUrlCacheContainerA (WININET.@)
3253 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3255 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3256 return NULL;
3259 /***********************************************************************
3260 * FindFirstUrlCacheContainerW (WININET.@)
3262 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3264 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3265 return NULL;
3268 /***********************************************************************
3269 * FindNextUrlCacheContainerA (WININET.@)
3271 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3273 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3274 return FALSE;
3277 /***********************************************************************
3278 * FindNextUrlCacheContainerW (WININET.@)
3280 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3282 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3283 return FALSE;
3286 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3287 LPCSTR lpszUrlSearchPattern,
3288 DWORD dwFlags,
3289 DWORD dwFilter,
3290 GROUPID GroupId,
3291 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3292 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3293 LPVOID lpReserved,
3294 LPDWORD pcbReserved2,
3295 LPVOID lpReserved3
3298 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3299 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3300 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3301 SetLastError(ERROR_FILE_NOT_FOUND);
3302 return NULL;
3305 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3306 LPCWSTR lpszUrlSearchPattern,
3307 DWORD dwFlags,
3308 DWORD dwFilter,
3309 GROUPID GroupId,
3310 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3311 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3312 LPVOID lpReserved,
3313 LPDWORD pcbReserved2,
3314 LPVOID lpReserved3
3317 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3318 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3319 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3320 SetLastError(ERROR_FILE_NOT_FOUND);
3321 return NULL;
3324 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3326 typedef struct URLCacheFindEntryHandle
3328 DWORD dwMagic;
3329 LPWSTR lpszUrlSearchPattern;
3330 DWORD dwContainerIndex;
3331 DWORD dwHashTableIndex;
3332 DWORD dwHashEntryIndex;
3333 } URLCacheFindEntryHandle;
3335 /***********************************************************************
3336 * FindFirstUrlCacheEntryA (WININET.@)
3339 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3340 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3342 URLCacheFindEntryHandle *pEntryHandle;
3344 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3346 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3347 if (!pEntryHandle)
3348 return NULL;
3350 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3351 if (lpszUrlSearchPattern)
3353 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3354 if (!pEntryHandle->lpszUrlSearchPattern)
3356 heap_free(pEntryHandle);
3357 return NULL;
3360 else
3361 pEntryHandle->lpszUrlSearchPattern = NULL;
3362 pEntryHandle->dwContainerIndex = 0;
3363 pEntryHandle->dwHashTableIndex = 0;
3364 pEntryHandle->dwHashEntryIndex = 0;
3366 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3368 heap_free(pEntryHandle);
3369 return NULL;
3371 return pEntryHandle;
3374 /***********************************************************************
3375 * FindFirstUrlCacheEntryW (WININET.@)
3378 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3379 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3381 URLCacheFindEntryHandle *pEntryHandle;
3383 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3385 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3386 if (!pEntryHandle)
3387 return NULL;
3389 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3390 if (lpszUrlSearchPattern)
3392 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3393 if (!pEntryHandle->lpszUrlSearchPattern)
3395 heap_free(pEntryHandle);
3396 return NULL;
3399 else
3400 pEntryHandle->lpszUrlSearchPattern = NULL;
3401 pEntryHandle->dwContainerIndex = 0;
3402 pEntryHandle->dwHashTableIndex = 0;
3403 pEntryHandle->dwHashEntryIndex = 0;
3405 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3407 heap_free(pEntryHandle);
3408 return NULL;
3410 return pEntryHandle;
3413 static BOOL FindNextUrlCacheEntryInternal(
3414 HANDLE hEnumHandle,
3415 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3416 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3417 BOOL unicode)
3419 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3420 URLCACHECONTAINER * pContainer;
3422 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3424 SetLastError(ERROR_INVALID_HANDLE);
3425 return FALSE;
3428 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3429 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3431 LPURLCACHE_HEADER pHeader;
3432 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3433 DWORD error;
3435 error = URLCacheContainer_OpenIndex(pContainer);
3436 if (error != ERROR_SUCCESS)
3438 SetLastError(error);
3439 return FALSE;
3442 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3443 return FALSE;
3445 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3446 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3448 const struct _HASH_ENTRY *pHashEntry = NULL;
3449 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3450 pEntryHandle->dwHashEntryIndex++)
3452 const URL_CACHEFILE_ENTRY *pUrlEntry;
3453 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3455 if (pEntry->dwSignature != URL_SIGNATURE)
3456 continue;
3458 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3459 TRACE("Found URL: %s\n",
3460 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3461 TRACE("Header info: %s\n",
3462 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3464 error = URLCache_CopyEntry(
3465 pContainer,
3466 pHeader,
3467 lpNextCacheEntryInfo,
3468 lpdwNextCacheEntryInfoBufferSize,
3469 pUrlEntry,
3470 unicode);
3471 if (error != ERROR_SUCCESS)
3473 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3474 SetLastError(error);
3475 return FALSE;
3477 TRACE("Local File Name: %s\n",
3478 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3480 /* increment the current index so that next time the function
3481 * is called the next entry is returned */
3482 pEntryHandle->dwHashEntryIndex++;
3483 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3484 return TRUE;
3488 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3491 SetLastError(ERROR_NO_MORE_ITEMS);
3492 return FALSE;
3495 /***********************************************************************
3496 * FindNextUrlCacheEntryA (WININET.@)
3498 BOOL WINAPI FindNextUrlCacheEntryA(
3499 HANDLE hEnumHandle,
3500 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3501 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3503 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3505 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3506 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3509 /***********************************************************************
3510 * FindNextUrlCacheEntryW (WININET.@)
3512 BOOL WINAPI FindNextUrlCacheEntryW(
3513 HANDLE hEnumHandle,
3514 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3515 LPDWORD lpdwNextCacheEntryInfoBufferSize
3518 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3520 return FindNextUrlCacheEntryInternal(hEnumHandle,
3521 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3522 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3525 /***********************************************************************
3526 * FindCloseUrlCache (WININET.@)
3528 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3530 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3532 TRACE("(%p)\n", hEnumHandle);
3534 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3536 SetLastError(ERROR_INVALID_HANDLE);
3537 return FALSE;
3540 pEntryHandle->dwMagic = 0;
3541 heap_free(pEntryHandle->lpszUrlSearchPattern);
3542 heap_free(pEntryHandle);
3543 return TRUE;
3546 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3547 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3549 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3550 dwSearchCondition, lpGroupId, lpReserved);
3551 return NULL;
3554 BOOL WINAPI FindNextUrlCacheEntryExA(
3555 HANDLE hEnumHandle,
3556 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3557 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3558 LPVOID lpReserved,
3559 LPDWORD pcbReserved2,
3560 LPVOID lpReserved3
3563 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3564 lpReserved, pcbReserved2, lpReserved3);
3565 return FALSE;
3568 BOOL WINAPI FindNextUrlCacheEntryExW(
3569 HANDLE hEnumHandle,
3570 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3571 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3572 LPVOID lpReserved,
3573 LPDWORD pcbReserved2,
3574 LPVOID lpReserved3
3577 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3578 lpReserved, pcbReserved2, lpReserved3);
3579 return FALSE;
3582 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3584 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3585 return FALSE;
3588 /***********************************************************************
3589 * CreateUrlCacheGroup (WININET.@)
3592 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3594 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3595 return FALSE;
3598 /***********************************************************************
3599 * DeleteUrlCacheGroup (WININET.@)
3602 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3604 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3605 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3606 return FALSE;
3609 /***********************************************************************
3610 * SetUrlCacheEntryGroupA (WININET.@)
3613 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3614 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3615 LPVOID lpReserved)
3617 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3618 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3619 pbGroupAttributes, cbGroupAttributes, lpReserved);
3620 SetLastError(ERROR_FILE_NOT_FOUND);
3621 return FALSE;
3624 /***********************************************************************
3625 * SetUrlCacheEntryGroupW (WININET.@)
3628 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3629 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3630 LPVOID lpReserved)
3632 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3633 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3634 pbGroupAttributes, cbGroupAttributes, lpReserved);
3635 SetLastError(ERROR_FILE_NOT_FOUND);
3636 return FALSE;
3639 /***********************************************************************
3640 * GetUrlCacheConfigInfoW (WININET.@)
3642 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3644 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3645 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3646 return FALSE;
3649 /***********************************************************************
3650 * GetUrlCacheConfigInfoA (WININET.@)
3652 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3654 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3655 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3656 return FALSE;
3659 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3660 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3661 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3663 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3664 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3665 lpdwGroupInfo, lpReserved);
3666 return FALSE;
3669 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3670 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3671 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3673 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3674 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3675 lpdwGroupInfo, lpReserved);
3676 return FALSE;
3679 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3680 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3682 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3683 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3684 return TRUE;
3687 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3688 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3690 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3691 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3692 return TRUE;
3695 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3697 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3698 return TRUE;
3701 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3703 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3704 return TRUE;
3707 /***********************************************************************
3708 * DeleteIE3Cache (WININET.@)
3710 * Deletes the files used by the IE3 URL caching system.
3712 * PARAMS
3713 * hWnd [I] A dummy window.
3714 * hInst [I] Instance of process calling the function.
3715 * lpszCmdLine [I] Options used by function.
3716 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3718 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3720 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3721 return 0;
3724 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3725 FILETIME *pftLastModified)
3727 BOOL ret;
3728 FILETIME now, expired;
3730 *pftLastModified = pUrlEntry->LastModifiedTime;
3731 GetSystemTimeAsFileTime(&now);
3732 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3733 pUrlEntry->wExpiredTime, &expired);
3734 /* If the expired time is 0, it's interpreted as not expired */
3735 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3736 ret = FALSE;
3737 else
3738 ret = CompareFileTime(&expired, &now) < 0;
3739 return ret;
3742 /***********************************************************************
3743 * IsUrlCacheEntryExpiredA (WININET.@)
3745 * PARAMS
3746 * url [I] Url
3747 * dwFlags [I] Unknown
3748 * pftLastModified [O] Last modified time
3750 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3752 LPURLCACHE_HEADER pHeader;
3753 struct _HASH_ENTRY * pHashEntry;
3754 const CACHEFILE_ENTRY * pEntry;
3755 const URL_CACHEFILE_ENTRY * pUrlEntry;
3756 URLCACHECONTAINER * pContainer;
3757 BOOL expired;
3759 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3761 if (!url || !pftLastModified)
3762 return TRUE;
3763 if (dwFlags)
3764 FIXME("unknown flags 0x%08x\n", dwFlags);
3766 /* Any error implies that the URL is expired, i.e. not in the cache */
3767 if (URLCacheContainers_FindContainerA(url, &pContainer))
3769 memset(pftLastModified, 0, sizeof(*pftLastModified));
3770 return TRUE;
3773 if (URLCacheContainer_OpenIndex(pContainer))
3775 memset(pftLastModified, 0, sizeof(*pftLastModified));
3776 return TRUE;
3779 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3781 memset(pftLastModified, 0, sizeof(*pftLastModified));
3782 return TRUE;
3785 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3787 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3788 memset(pftLastModified, 0, sizeof(*pftLastModified));
3789 TRACE("entry %s not found!\n", url);
3790 return TRUE;
3793 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3794 if (pEntry->dwSignature != URL_SIGNATURE)
3796 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3797 memset(pftLastModified, 0, sizeof(*pftLastModified));
3798 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3799 return TRUE;
3802 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3803 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3805 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3807 return expired;
3810 /***********************************************************************
3811 * IsUrlCacheEntryExpiredW (WININET.@)
3813 * PARAMS
3814 * url [I] Url
3815 * dwFlags [I] Unknown
3816 * pftLastModified [O] Last modified time
3818 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3820 LPURLCACHE_HEADER pHeader;
3821 struct _HASH_ENTRY * pHashEntry;
3822 const CACHEFILE_ENTRY * pEntry;
3823 const URL_CACHEFILE_ENTRY * pUrlEntry;
3824 URLCACHECONTAINER * pContainer;
3825 BOOL expired;
3827 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3829 if (!url || !pftLastModified)
3830 return TRUE;
3831 if (dwFlags)
3832 FIXME("unknown flags 0x%08x\n", dwFlags);
3834 /* Any error implies that the URL is expired, i.e. not in the cache */
3835 if (URLCacheContainers_FindContainerW(url, &pContainer))
3837 memset(pftLastModified, 0, sizeof(*pftLastModified));
3838 return TRUE;
3841 if (URLCacheContainer_OpenIndex(pContainer))
3843 memset(pftLastModified, 0, sizeof(*pftLastModified));
3844 return TRUE;
3847 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3849 memset(pftLastModified, 0, sizeof(*pftLastModified));
3850 return TRUE;
3853 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3855 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3856 memset(pftLastModified, 0, sizeof(*pftLastModified));
3857 TRACE("entry %s not found!\n", debugstr_w(url));
3858 return TRUE;
3861 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3863 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3864 memset(pftLastModified, 0, sizeof(*pftLastModified));
3865 TRACE("entry %s not found!\n", debugstr_w(url));
3866 return TRUE;
3869 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3870 if (pEntry->dwSignature != URL_SIGNATURE)
3872 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3873 memset(pftLastModified, 0, sizeof(*pftLastModified));
3874 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3875 return TRUE;
3878 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3879 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3881 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3883 return expired;
3886 /***********************************************************************
3887 * GetDiskInfoA (WININET.@)
3889 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3891 BOOL ret;
3892 ULARGE_INTEGER bytes_free, bytes_total;
3894 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3896 if (!path)
3898 SetLastError(ERROR_INVALID_PARAMETER);
3899 return FALSE;
3902 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3904 if (cluster_size) *cluster_size = 1;
3905 if (free) *free = bytes_free.QuadPart;
3906 if (total) *total = bytes_total.QuadPart;
3908 return ret;
3911 /***********************************************************************
3912 * RegisterUrlCacheNotification (WININET.@)
3914 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3916 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3917 return 0;
3920 /***********************************************************************
3921 * IncrementUrlCacheHeaderData (WININET.@)
3923 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3925 FIXME("(%u, %p)\n", index, data);
3926 return FALSE;