d3d9: Fix a few ok conditions in the visual tests.
[wine.git] / dlls / wininet / urlcache.c
blob429867a7dc90359c69d76911923529985279e485
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 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 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winuser.h"
40 #include "wininet.h"
41 #include "winineti.h"
42 #include "winerror.h"
43 #include "internet.h"
44 #include "winreg.h"
45 #include "shlwapi.h"
46 #include "shlobj.h"
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
53 #define ENTRY_START_OFFSET 0x4000
54 #define DIR_LENGTH 8
55 #define BLOCKSIZE 128
56 #define HASHTABLE_SIZE 448
57 #define HASHTABLE_BLOCKSIZE 7
58 #define HASHTABLE_FREE 3
59 #define ALLOCATION_TABLE_OFFSET 0x250
60 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
61 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
62 #define NEWFILE_NUM_BLOCKS 0xd80
63 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
65 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
66 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
67 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
68 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
69 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
71 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
73 typedef struct _CACHEFILE_ENTRY
75 /* union
76 {*/
77 DWORD dwSignature; /* e.g. "URL " */
78 /* CHAR szSignature[4];
79 };*/
80 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
81 } CACHEFILE_ENTRY;
83 typedef struct _URL_CACHEFILE_ENTRY
85 CACHEFILE_ENTRY CacheFileEntry;
86 FILETIME LastModifiedTime;
87 FILETIME LastAccessTime;
88 WORD wExpiredDate; /* expire date in dos format */
89 WORD wExpiredTime; /* expire time in dos format */
90 DWORD dwUnknown1; /* usually zero */
91 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
92 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
93 DWORD dwUnknown2; /* usually zero */
94 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
95 DWORD dwUnknown3; /* usually 0x60 */
96 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
97 BYTE CacheDir; /* index of cache directory this url is stored in */
98 BYTE Unknown4; /* usually zero */
99 WORD wUnknown5; /* usually 0x1010 */
100 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
101 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
102 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
103 DWORD dwHeaderInfoSize;
104 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
105 WORD wLastSyncDate; /* last sync date in dos format */
106 WORD wLastSyncTime; /* last sync time in dos format */
107 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
108 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109 WORD wUnknownDate; /* usually same as wLastSyncDate */
110 WORD wUnknownTime; /* usually same as wLastSyncTime */
111 DWORD dwUnknown7; /* usually zero */
112 DWORD dwUnknown8; /* usually zero */
113 /* packing to dword align start of next field */
114 /* CHAR szSourceUrlName[]; (url) */
115 /* packing to dword align start of next field */
116 /* CHAR szLocalFileName[]; (local file name excluding path) */
117 /* packing to dword align start of next field */
118 /* CHAR szHeaderInfo[]; (header info) */
119 } URL_CACHEFILE_ENTRY;
121 struct _HASH_ENTRY
123 DWORD dwHashKey;
124 DWORD dwOffsetEntry;
127 typedef struct _HASH_CACHEFILE_ENTRY
129 CACHEFILE_ENTRY CacheFileEntry;
130 DWORD dwAddressNext;
131 DWORD dwHashTableNumber;
132 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
133 } HASH_CACHEFILE_ENTRY;
135 typedef struct _DIRECTORY_DATA
137 DWORD dwUnknown;
138 char filename[DIR_LENGTH];
139 } DIRECTORY_DATA;
141 typedef struct _URLCACHE_HEADER
143 char szSignature[28];
144 DWORD dwFileSize;
145 DWORD dwOffsetFirstHashTable;
146 DWORD dwIndexCapacityInBlocks;
147 DWORD dwBlocksInUse;
148 DWORD dwUnknown1;
149 DWORD dwCacheLimitLow; /* disk space limit for cache */
150 DWORD dwCacheLimitHigh; /* disk space limit for cache */
151 DWORD dwUnknown4; /* current disk space usage for cache */
152 DWORD dwUnknown5; /* current disk space usage for cache */
153 DWORD dwUnknown6; /* possibly a flag? */
154 DWORD dwUnknown7;
155 BYTE DirectoryCount; /* number of directory_data's */
156 BYTE Unknown8[3]; /* just padding? */
157 DIRECTORY_DATA directory_data[1]; /* first directory entry */
158 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
159 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
161 typedef struct _STREAM_HANDLE
163 HANDLE hFile;
164 CHAR lpszUrl[1];
165 } STREAM_HANDLE;
167 typedef struct _URLCACHECONTAINER
169 struct list entry; /* part of a list */
170 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
171 LPWSTR path; /* path to url container directory */
172 HANDLE hMapping; /* handle of file mapping */
173 DWORD file_size; /* size of file when mapping was opened */
174 HANDLE hMutex; /* handle of mutex */
175 } URLCACHECONTAINER;
178 /* List of all containers available */
179 static struct list UrlContainers = LIST_INIT(UrlContainers);
181 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash);
183 /***********************************************************************
184 * URLCache_PathToObjectName (Internal)
186 * Converts a path to a name suitable for use as a Win32 object name.
187 * Replaces '\\' characters in-place with the specified character
188 * (usually '_' or '!')
190 * RETURNS
191 * nothing
194 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
196 for (; *lpszPath; lpszPath++)
198 if (*lpszPath == '\\')
199 *lpszPath = replace;
203 /***********************************************************************
204 * URLCacheContainer_OpenIndex (Internal)
206 * Opens the index file and saves mapping handle in hCacheIndexMapping
208 * RETURNS
209 * TRUE if succeeded
210 * FALSE if failed
213 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
215 HANDLE hFile;
216 WCHAR wszFilePath[MAX_PATH];
217 DWORD dwFileSize;
219 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
220 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
222 if (pContainer->hMapping)
223 return TRUE;
225 strcpyW(wszFilePath, pContainer->path);
226 strcatW(wszFilePath, wszIndex);
228 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
229 if (hFile == INVALID_HANDLE_VALUE)
231 /* Maybe the directory wasn't there? Try to create it */
232 if (CreateDirectoryW(pContainer->path, 0))
233 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
235 if (hFile == INVALID_HANDLE_VALUE)
237 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
238 return FALSE;
241 /* At this stage we need the mutex because we may be about to create the
242 * file.
244 WaitForSingleObject(pContainer->hMutex, INFINITE);
246 dwFileSize = GetFileSize(hFile, NULL);
247 if (dwFileSize == INVALID_FILE_SIZE)
249 ReleaseMutex(pContainer->hMutex);
250 return FALSE;
253 if (dwFileSize == 0)
255 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
256 HKEY key;
257 char achZeroes[0x1000];
258 DWORD dwOffset;
259 DWORD dwError = 0;
261 /* Write zeroes to the entire file so we can safely map it without
262 * fear of getting a SEGV because the disk is full.
264 memset(achZeroes, 0, sizeof(achZeroes));
265 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
267 DWORD dwWrite = sizeof(achZeroes);
268 DWORD dwWritten;
270 if (NEWFILE_SIZE - dwOffset < dwWrite)
271 dwWrite = NEWFILE_SIZE - dwOffset;
272 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
273 dwWritten != dwWrite)
275 /* If we fail to write, we need to return the error that
276 * cause the problem and also make sure the file is no
277 * longer there, if possible.
279 dwError = GetLastError();
281 break;
285 if (!dwError)
287 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
289 if (hMapping)
291 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
293 if (pHeader)
295 WCHAR *pwchDir;
296 WCHAR wszDirPath[MAX_PATH];
297 FILETIME ft;
298 int i, j;
300 dwFileSize = NEWFILE_SIZE;
302 /* First set some constants and defaults in the header */
303 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
304 pHeader->dwFileSize = dwFileSize;
305 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
306 /* 127MB - taken from default for Windows 2000 */
307 pHeader->dwCacheLimitHigh = 0;
308 pHeader->dwCacheLimitLow = 0x07ff5400;
309 /* Copied from a Windows 2000 cache index */
310 pHeader->DirectoryCount = 4;
312 /* If the registry has a cache size set, use the registry value */
313 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
315 DWORD dw;
316 DWORD len = sizeof(dw);
317 DWORD keytype;
319 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
320 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
321 keytype == REG_DWORD)
323 pHeader->dwCacheLimitHigh = (dw >> 22);
324 pHeader->dwCacheLimitLow = dw << 10;
326 RegCloseKey(key);
329 URLCache_CreateHashTable(pHeader, NULL);
331 /* Last step - create the directories */
333 strcpyW(wszDirPath, pContainer->path);
334 pwchDir = wszDirPath + strlenW(wszDirPath);
335 pwchDir[8] = 0;
337 GetSystemTimeAsFileTime(&ft);
339 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
341 /* The following values were copied from a Windows index.
342 * I don't know what the values are supposed to mean but
343 * have made them the same in the hope that this will
344 * be better for compatibility
346 pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
347 for (j = 0;; ++j)
349 int k;
350 ULONGLONG n = ft.dwHighDateTime;
352 /* Generate a file name to attempt to create.
353 * This algorithm will create what will appear
354 * to be random and unrelated directory names
355 * of up to 9 characters in length.
357 n <<= 32;
358 n += ft.dwLowDateTime;
359 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
361 for (k = 0; k < 8; ++k)
363 int r = (n % 36);
365 /* Dividing by a prime greater than 36 helps
366 * with the appearance of randomness
368 n /= 37;
370 if (r < 10)
371 pwchDir[k] = '0' + r;
372 else
373 pwchDir[k] = 'A' + (r - 10);
376 if (CreateDirectoryW(wszDirPath, 0))
378 /* The following is OK because we generated an
379 * 8 character directory name made from characters
380 * [A-Z0-9], which are equivalent for all code
381 * pages and for UTF-16
383 for (k = 0; k < 8; ++k)
384 pHeader->directory_data[i].filename[k] = pwchDir[k];
385 break;
387 else if (j >= 255)
389 /* Give up. The most likely cause of this
390 * is a full disk, but whatever the cause
391 * is, it should be more than apparent that
392 * we won't succeed.
394 dwError = GetLastError();
395 break;
400 UnmapViewOfFile(pHeader);
402 else
404 dwError = GetLastError();
406 CloseHandle(hMapping);
408 else
410 dwError = GetLastError();
414 if (dwError)
416 CloseHandle(hFile);
417 DeleteFileW(wszFilePath);
418 ReleaseMutex(pContainer->hMutex);
419 SetLastError(dwError);
420 return FALSE;
425 ReleaseMutex(pContainer->hMutex);
427 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
428 URLCache_PathToObjectName(wszFilePath, '_');
429 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
430 if (!pContainer->hMapping)
431 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
432 CloseHandle(hFile);
433 if (!pContainer->hMapping)
435 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
436 return FALSE;
439 return TRUE;
442 /***********************************************************************
443 * URLCacheContainer_CloseIndex (Internal)
445 * Closes the index
447 * RETURNS
448 * nothing
451 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
453 CloseHandle(pContainer->hMapping);
454 pContainer->hMapping = NULL;
457 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
459 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
460 int path_len = strlenW(path);
461 int cache_prefix_len = strlenW(cache_prefix);
463 if (!pContainer)
465 return FALSE;
468 pContainer->hMapping = NULL;
469 pContainer->file_size = 0;
471 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
472 if (!pContainer->path)
474 HeapFree(GetProcessHeap(), 0, pContainer);
475 return FALSE;
478 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
480 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
481 if (!pContainer->cache_prefix)
483 HeapFree(GetProcessHeap(), 0, pContainer->path);
484 HeapFree(GetProcessHeap(), 0, pContainer);
485 return FALSE;
488 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
490 CharLowerW(mutex_name);
491 URLCache_PathToObjectName(mutex_name, '!');
493 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
495 ERR("couldn't create mutex (error is %d)\n", GetLastError());
496 HeapFree(GetProcessHeap(), 0, pContainer->path);
497 HeapFree(GetProcessHeap(), 0, pContainer);
498 return FALSE;
501 list_add_head(&UrlContainers, &pContainer->entry);
503 return TRUE;
506 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
508 list_remove(&pContainer->entry);
510 URLCacheContainer_CloseIndex(pContainer);
511 CloseHandle(pContainer->hMutex);
512 HeapFree(GetProcessHeap(), 0, pContainer->path);
513 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
514 HeapFree(GetProcessHeap(), 0, pContainer);
517 void URLCacheContainers_CreateDefaults(void)
519 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
520 static const WCHAR UrlPrefix[] = {0};
521 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
522 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
523 static const WCHAR CookieSuffix[] = {0};
524 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
525 static const struct
527 int nFolder; /* CSIDL_* constant */
528 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
529 const WCHAR * cache_prefix; /* prefix used to reference the container */
530 } DefaultContainerData[] =
532 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
533 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
534 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
536 DWORD i;
538 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
540 WCHAR wszCachePath[MAX_PATH];
541 WCHAR wszMutexName[MAX_PATH];
542 int path_len, suffix_len;
544 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
546 ERR("Couldn't get path for default container %u\n", i);
547 continue;
549 path_len = strlenW(wszCachePath);
550 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
552 if (path_len + suffix_len + 2 > MAX_PATH)
554 ERR("Path too long\n");
555 continue;
558 wszCachePath[path_len] = '\\';
559 wszCachePath[path_len+1] = 0;
561 strcpyW(wszMutexName, wszCachePath);
563 if (suffix_len)
565 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
566 wszCachePath[path_len + suffix_len + 1] = '\\';
567 wszCachePath[path_len + suffix_len + 2] = '\0';
570 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
574 void URLCacheContainers_DeleteAll(void)
576 while(!list_empty(&UrlContainers))
577 URLCacheContainer_DeleteContainer(
578 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
582 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
584 URLCACHECONTAINER * pContainer;
586 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
588 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
590 int prefix_len = strlenW(pContainer->cache_prefix);
591 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
593 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
594 *ppContainer = pContainer;
595 return TRUE;
598 ERR("no container found\n");
599 SetLastError(ERROR_FILE_NOT_FOUND);
600 return FALSE;
603 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
605 BOOL ret;
606 LPWSTR lpwszUrl;
607 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
608 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
610 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
611 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
612 HeapFree(GetProcessHeap(), 0, lpwszUrl);
613 return ret;
615 return FALSE;
618 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
620 DWORD i = 0;
621 URLCACHECONTAINER * pContainer;
623 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
625 /* non-NULL search pattern only returns one container ever */
626 if (lpwszSearchPattern && dwIndex > 0)
627 return FALSE;
629 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
631 if (lpwszSearchPattern)
633 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
635 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
636 *ppContainer = pContainer;
637 return TRUE;
640 else
642 if (i == dwIndex)
644 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
645 *ppContainer = pContainer;
646 return TRUE;
649 i++;
651 return FALSE;
654 /***********************************************************************
655 * URLCacheContainer_LockIndex (Internal)
658 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
660 BYTE index;
661 LPVOID pIndexData;
662 URLCACHE_HEADER * pHeader;
664 /* acquire mutex */
665 WaitForSingleObject(pContainer->hMutex, INFINITE);
667 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
669 if (!pIndexData)
671 ReleaseMutex(pContainer->hMutex);
672 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
673 return FALSE;
675 pHeader = (URLCACHE_HEADER *)pIndexData;
677 /* file has grown - we need to remap to prevent us getting
678 * access violations when we try and access beyond the end
679 * of the memory mapped file */
680 if (pHeader->dwFileSize != pContainer->file_size)
682 URLCacheContainer_CloseIndex(pContainer);
683 if (!URLCacheContainer_OpenIndex(pContainer))
685 ReleaseMutex(pContainer->hMutex);
686 return FALSE;
688 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
690 if (!pIndexData)
692 ReleaseMutex(pContainer->hMutex);
693 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
694 return FALSE;
696 pHeader = (URLCACHE_HEADER *)pIndexData;
699 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
701 for (index = 0; index < pHeader->DirectoryCount; index++)
703 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
706 return pHeader;
709 /***********************************************************************
710 * URLCacheContainer_UnlockIndex (Internal)
713 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
715 /* release mutex */
716 ReleaseMutex(pContainer->hMutex);
717 return UnmapViewOfFile(pHeader);
721 #ifndef CHAR_BIT
722 #define CHAR_BIT (8 * sizeof(CHAR))
723 #endif
725 /***********************************************************************
726 * URLCache_Allocation_BlockIsFree (Internal)
728 * Is the specified block number free?
730 * RETURNS
731 * zero if free
732 * non-zero otherwise
735 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
737 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
738 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
741 /***********************************************************************
742 * URLCache_Allocation_BlockFree (Internal)
744 * Marks the specified block as free
746 * RETURNS
747 * nothing
750 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
752 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
753 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
756 /***********************************************************************
757 * URLCache_Allocation_BlockAllocate (Internal)
759 * Marks the specified block as allocated
761 * RETURNS
762 * nothing
765 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
767 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
768 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
771 /***********************************************************************
772 * URLCache_FindFirstFreeEntry (Internal)
774 * Finds and allocates the first block of free space big enough and
775 * sets ppEntry to point to it.
777 * RETURNS
778 * TRUE if it had enough space
779 * FALSE if it couldn't find enough space
782 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
784 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
785 DWORD dwBlockNumber;
786 DWORD dwFreeCounter;
787 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
789 for (dwFreeCounter = 0;
790 dwFreeCounter < dwBlocksNeeded &&
791 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
792 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
793 dwFreeCounter++)
794 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
796 if (dwFreeCounter == dwBlocksNeeded)
798 DWORD index;
799 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
800 for (index = 0; index < dwBlocksNeeded; index++)
801 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
802 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
803 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
804 return TRUE;
807 FIXME("Grow file\n");
808 return FALSE;
811 /***********************************************************************
812 * URLCache_DeleteEntry (Internal)
814 * Deletes the specified entry and frees the space allocated to it
816 * RETURNS
817 * TRUE if it succeeded
818 * FALSE if it failed
821 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
823 DWORD dwStartBlock;
824 DWORD dwBlock;
825 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
827 /* update allocation table */
828 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
829 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
830 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
832 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
833 return TRUE;
836 /***********************************************************************
837 * URLCache_LocalFileNameToPathW (Internal)
839 * Copies the full path to the specified buffer given the local file
840 * name and the index of the directory it is in. Always sets value in
841 * lpBufferSize to the required buffer size (in bytes).
843 * RETURNS
844 * TRUE if the buffer was big enough
845 * FALSE if the buffer was too small
848 static BOOL URLCache_LocalFileNameToPathW(
849 const URLCACHECONTAINER * pContainer,
850 LPCURLCACHE_HEADER pHeader,
851 LPCSTR szLocalFileName,
852 BYTE Directory,
853 LPWSTR wszPath,
854 LPLONG lpBufferSize)
856 LONG nRequired;
857 int path_len = strlenW(pContainer->path);
858 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
859 if (Directory >= pHeader->DirectoryCount)
861 *lpBufferSize = 0;
862 return FALSE;
865 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
866 if (nRequired < *lpBufferSize)
868 int dir_len;
870 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
871 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
872 wszPath[dir_len + path_len] = '\\';
873 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
874 *lpBufferSize = nRequired;
875 return TRUE;
877 *lpBufferSize = nRequired;
878 return FALSE;
881 /***********************************************************************
882 * URLCache_LocalFileNameToPathA (Internal)
884 * Copies the full path to the specified buffer given the local file
885 * name and the index of the directory it is in. Always sets value in
886 * lpBufferSize to the required buffer size.
888 * RETURNS
889 * TRUE if the buffer was big enough
890 * FALSE if the buffer was too small
893 static BOOL URLCache_LocalFileNameToPathA(
894 const URLCACHECONTAINER * pContainer,
895 LPCURLCACHE_HEADER pHeader,
896 LPCSTR szLocalFileName,
897 BYTE Directory,
898 LPSTR szPath,
899 LPLONG lpBufferSize)
901 LONG nRequired;
902 int path_len, file_name_len, dir_len;
904 if (Directory >= pHeader->DirectoryCount)
906 *lpBufferSize = 0;
907 return FALSE;
910 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
911 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
912 dir_len = DIR_LENGTH;
914 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
915 if (nRequired < *lpBufferSize)
917 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
918 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
919 szPath[path_len + dir_len] = '\\';
920 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
921 *lpBufferSize = nRequired;
922 return TRUE;
924 *lpBufferSize = nRequired;
925 return FALSE;
928 /***********************************************************************
929 * URLCache_CopyEntry (Internal)
931 * Copies an entry from the cache index file to the Win32 structure
933 * RETURNS
934 * TRUE if the buffer was big enough
935 * FALSE if the buffer was too small
938 static BOOL URLCache_CopyEntry(
939 URLCACHECONTAINER * pContainer,
940 LPCURLCACHE_HEADER pHeader,
941 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
942 LPDWORD lpdwBufferSize,
943 const URL_CACHEFILE_ENTRY * pUrlEntry,
944 BOOL bUnicode)
946 int lenUrl;
947 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
949 if (*lpdwBufferSize >= dwRequiredSize)
951 lpCacheEntryInfo->lpHeaderInfo = NULL;
952 lpCacheEntryInfo->lpszFileExtension = NULL;
953 lpCacheEntryInfo->lpszLocalFileName = NULL;
954 lpCacheEntryInfo->lpszSourceUrlName = NULL;
955 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
956 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
957 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
958 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
959 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
960 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
961 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
962 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
963 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
964 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
965 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
966 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
967 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
968 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
971 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
972 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
973 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
974 if (bUnicode)
975 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
976 else
977 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
978 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
980 /* FIXME: is source url optional? */
981 if (*lpdwBufferSize >= dwRequiredSize)
983 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
984 if (bUnicode)
985 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
986 else
987 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
990 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
991 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
992 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
994 if (pUrlEntry->dwOffsetLocalName)
996 LONG nLocalFilePathSize;
997 LPSTR lpszLocalFileName;
998 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
999 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1000 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1001 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
1003 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1005 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1007 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1008 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1009 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1011 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1013 if (*lpdwBufferSize >= dwRequiredSize)
1015 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1016 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1017 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1019 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1020 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1021 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1023 if (pUrlEntry->dwOffsetFileExtension)
1025 int lenExtension;
1027 if (bUnicode)
1028 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1029 else
1030 lenExtension = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1031 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1033 if (*lpdwBufferSize >= dwRequiredSize)
1035 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1036 if (bUnicode)
1037 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1038 else
1039 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1042 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1043 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1044 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1047 if (dwRequiredSize > *lpdwBufferSize)
1049 *lpdwBufferSize = dwRequiredSize;
1050 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1051 return FALSE;
1053 *lpdwBufferSize = dwRequiredSize;
1054 return TRUE;
1058 /***********************************************************************
1059 * URLCache_SetEntryInfo (Internal)
1061 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1062 * according to the flags set by dwFieldControl.
1064 * RETURNS
1065 * TRUE if the buffer was big enough
1066 * FALSE if the buffer was too small
1069 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1071 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1072 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1073 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1074 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1075 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1076 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1077 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1078 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1079 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1080 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1081 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1082 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1083 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1084 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1085 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1086 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1088 return TRUE;
1091 /***********************************************************************
1092 * URLCache_HashKey (Internal)
1094 * Returns the hash key for a given string
1096 * RETURNS
1097 * hash key for the string
1100 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1102 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1103 * but the algorithm and result are not the same!
1105 static const unsigned char lookupTable[256] =
1107 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1108 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1109 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1110 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1111 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1112 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1113 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1114 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1115 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1116 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1117 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1118 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1119 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1120 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1121 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1122 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1123 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1124 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1125 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1126 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1127 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1128 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1129 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1130 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1131 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1132 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1133 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1134 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1135 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1136 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1137 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1138 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1140 BYTE key[4];
1141 DWORD i;
1142 int subscript[sizeof(key) / sizeof(key[0])];
1144 subscript[0] = *lpszKey;
1145 subscript[1] = (char)(*lpszKey + 1);
1146 subscript[2] = (char)(*lpszKey + 2);
1147 subscript[3] = (char)(*lpszKey + 3);
1149 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1150 key[i] = lookupTable[i];
1152 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1154 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1155 key[i] = lookupTable[*lpszKey ^ key[i]];
1158 return *(DWORD *)key;
1161 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1163 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1166 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1168 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1169 return ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) &&
1170 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1173 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1175 /* structure of hash table:
1176 * 448 entries divided into 64 blocks
1177 * each block therefore contains a chain of 7 key/offset pairs
1178 * how position in table is calculated:
1179 * 1. the url is hashed in helper function
1180 * 2. the key % 64 * 8 is the offset
1181 * 3. the key in the hash table is the hash key aligned to 64
1183 * note:
1184 * there can be multiple hash tables in the file and the offset to
1185 * the next one is stored in the header of the hash table
1187 DWORD key = URLCache_HashKey(lpszUrl);
1188 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1189 HASH_CACHEFILE_ENTRY * pHashEntry;
1190 DWORD dwHashTableNumber = 0;
1192 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1194 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1195 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1196 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1198 int i;
1199 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1201 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1202 continue;
1204 /* make sure that it is in fact a hash entry */
1205 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1207 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1208 continue;
1211 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1213 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1214 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1216 /* FIXME: we should make sure that this is the right element
1217 * before returning and claiming that it is. We can do this
1218 * by doing a simple compare between the URL we were given
1219 * and the URL stored in the entry. However, this assumes
1220 * we know the format of all the entries stored in the
1221 * hash table */
1222 *ppHashEntry = pHashElement;
1223 return TRUE;
1227 return FALSE;
1230 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1232 LPSTR urlA;
1233 int url_len;
1234 BOOL ret;
1236 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1237 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1238 if (!urlA)
1240 SetLastError(ERROR_OUTOFMEMORY);
1241 return FALSE;
1243 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, urlA, url_len, NULL, NULL);
1244 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1245 HeapFree(GetProcessHeap(), 0, urlA);
1246 return ret;
1249 /***********************************************************************
1250 * URLCache_HashEntrySetUse (Internal)
1252 * Searches all the hash tables in the index for the given URL and
1253 * sets the use count (stored or'ed with key)
1255 * RETURNS
1256 * TRUE if the entry was found
1257 * FALSE if the entry could not be found
1260 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1262 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1263 return TRUE;
1266 /***********************************************************************
1267 * URLCache_DeleteEntryFromHash (Internal)
1269 * Searches all the hash tables in the index for the given URL and
1270 * then if found deletes the entry.
1272 * RETURNS
1273 * TRUE if the entry was found
1274 * FALSE if the entry could not be found
1277 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1279 pHashEntry->dwHashKey = HASHTABLE_FREE;
1280 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1281 return TRUE;
1284 /***********************************************************************
1285 * URLCache_AddEntryToHash (Internal)
1287 * Searches all the hash tables for a free slot based on the offset
1288 * generated from the hash key. If a free slot is found, the offset and
1289 * key are entered into the hash table.
1291 * RETURNS
1292 * TRUE if the entry was added
1293 * FALSE if the entry could not be added
1296 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1298 /* see URLCache_FindEntryInHash for structure of hash tables */
1300 DWORD key = URLCache_HashKey(lpszUrl);
1301 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1302 HASH_CACHEFILE_ENTRY * pHashEntry;
1303 DWORD dwHashTableNumber = 0;
1305 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1307 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1308 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1309 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1311 int i;
1312 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1314 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1315 break;
1317 /* make sure that it is in fact a hash entry */
1318 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1320 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1321 break;
1324 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1326 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1327 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1329 pHashElement->dwHashKey = key;
1330 pHashElement->dwOffsetEntry = dwOffsetEntry;
1331 return TRUE;
1335 pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1336 if (!pHashEntry)
1337 return FALSE;
1339 pHashEntry->HashTable[offset].dwHashKey = key;
1340 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1341 return TRUE;
1344 /***********************************************************************
1345 * URLCache_CreateHashTable (Internal)
1347 * Creates a new hash table in free space and adds it to the chain of existing
1348 * hash tables.
1350 * RETURNS
1351 * TRUE if the hash table was created
1352 * FALSE if the hash table could not be created
1355 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1357 HASH_CACHEFILE_ENTRY *pHash;
1358 DWORD dwOffset;
1359 int i;
1361 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1363 FIXME("no free space for hash table\n");
1364 SetLastError(ERROR_DISK_FULL);
1365 return NULL;
1368 dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1370 if (pPrevHash)
1371 pPrevHash->dwAddressNext = dwOffset;
1372 else
1373 pHeader->dwOffsetFirstHashTable = dwOffset;
1374 pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1375 pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1376 pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1377 for (i = 0; i < HASHTABLE_SIZE; i++)
1379 pHash->HashTable[i].dwOffsetEntry = 0;
1380 pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1382 return pHash;
1385 /***********************************************************************
1386 * URLCache_EnumHashTables (Internal)
1388 * Enumerates the hash tables in a container.
1390 * RETURNS
1391 * TRUE if an entry was found
1392 * FALSE if there are no more tables to enumerate.
1395 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1397 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1398 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1399 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1401 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1402 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1403 continue;
1404 /* make sure that it is in fact a hash entry */
1405 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1407 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1408 (*pdwHashTableNumber)++;
1409 continue;
1412 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1413 return TRUE;
1415 return FALSE;
1418 /***********************************************************************
1419 * URLCache_EnumHashTableEntries (Internal)
1421 * Enumerates entries in a hash table and returns the next non-free entry.
1423 * RETURNS
1424 * TRUE if an entry was found
1425 * FALSE if the hash table is empty or there are no more entries to
1426 * enumerate.
1429 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1430 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1432 for (; *index < HASHTABLE_SIZE ; (*index)++)
1434 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1435 continue;
1437 *ppHashEntry = &pHashEntry->HashTable[*index];
1438 TRACE("entry found %d\n", *index);
1439 return TRUE;
1441 TRACE("no more entries (%d)\n", *index);
1442 return FALSE;
1445 /***********************************************************************
1446 * GetUrlCacheEntryInfoExA (WININET.@)
1449 BOOL WINAPI GetUrlCacheEntryInfoExA(
1450 LPCSTR lpszUrl,
1451 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1452 LPDWORD lpdwCacheEntryInfoBufSize,
1453 LPSTR lpszReserved,
1454 LPDWORD lpdwReserved,
1455 LPVOID lpReserved,
1456 DWORD dwFlags)
1458 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1459 debugstr_a(lpszUrl),
1460 lpCacheEntryInfo,
1461 lpdwCacheEntryInfoBufSize,
1462 lpszReserved,
1463 lpdwReserved,
1464 lpReserved,
1465 dwFlags);
1467 if ((lpszReserved != NULL) ||
1468 (lpdwReserved != NULL) ||
1469 (lpReserved != NULL))
1471 ERR("Reserved value was not 0\n");
1472 SetLastError(ERROR_INVALID_PARAMETER);
1473 return FALSE;
1475 if (dwFlags != 0)
1476 FIXME("Undocumented flag(s): %x\n", dwFlags);
1477 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1480 /***********************************************************************
1481 * GetUrlCacheEntryInfoA (WININET.@)
1484 BOOL WINAPI GetUrlCacheEntryInfoA(
1485 IN LPCSTR lpszUrlName,
1486 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1487 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1490 LPURLCACHE_HEADER pHeader;
1491 struct _HASH_ENTRY * pHashEntry;
1492 const CACHEFILE_ENTRY * pEntry;
1493 const URL_CACHEFILE_ENTRY * pUrlEntry;
1494 URLCACHECONTAINER * pContainer;
1496 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1498 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1499 return FALSE;
1501 if (!URLCacheContainer_OpenIndex(pContainer))
1502 return FALSE;
1504 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1505 return FALSE;
1507 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1509 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1510 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1511 SetLastError(ERROR_FILE_NOT_FOUND);
1512 return FALSE;
1515 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1516 if (pEntry->dwSignature != URL_SIGNATURE)
1518 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1519 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1520 SetLastError(ERROR_FILE_NOT_FOUND);
1521 return FALSE;
1524 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1525 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1526 if (pUrlEntry->dwOffsetHeaderInfo)
1527 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1529 if (!URLCache_CopyEntry(
1530 pContainer,
1531 pHeader,
1532 lpCacheEntryInfo,
1533 lpdwCacheEntryInfoBufferSize,
1534 pUrlEntry,
1535 FALSE /* ANSI */))
1537 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1538 return FALSE;
1540 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1542 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1544 return TRUE;
1547 /***********************************************************************
1548 * GetUrlCacheEntryInfoW (WININET.@)
1551 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1552 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1553 LPDWORD lpdwCacheEntryInfoBufferSize)
1555 LPURLCACHE_HEADER pHeader;
1556 struct _HASH_ENTRY * pHashEntry;
1557 const CACHEFILE_ENTRY * pEntry;
1558 const URL_CACHEFILE_ENTRY * pUrlEntry;
1559 URLCACHECONTAINER * pContainer;
1561 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1563 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1564 return FALSE;
1566 if (!URLCacheContainer_OpenIndex(pContainer))
1567 return FALSE;
1569 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1570 return FALSE;
1572 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1574 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1575 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1576 SetLastError(ERROR_FILE_NOT_FOUND);
1577 return FALSE;
1580 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1581 if (pEntry->dwSignature != URL_SIGNATURE)
1583 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1584 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1585 SetLastError(ERROR_FILE_NOT_FOUND);
1586 return FALSE;
1589 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1590 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1591 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1593 if (!URLCache_CopyEntry(
1594 pContainer,
1595 pHeader,
1596 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1597 lpdwCacheEntryInfoBufferSize,
1598 pUrlEntry,
1599 TRUE /* UNICODE */))
1601 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1602 return FALSE;
1604 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1606 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1608 return TRUE;
1611 /***********************************************************************
1612 * GetUrlCacheEntryInfoExW (WININET.@)
1615 BOOL WINAPI GetUrlCacheEntryInfoExW(
1616 LPCWSTR lpszUrl,
1617 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1618 LPDWORD lpdwCacheEntryInfoBufSize,
1619 LPWSTR lpszReserved,
1620 LPDWORD lpdwReserved,
1621 LPVOID lpReserved,
1622 DWORD dwFlags)
1624 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1625 debugstr_w(lpszUrl),
1626 lpCacheEntryInfo,
1627 lpdwCacheEntryInfoBufSize,
1628 lpszReserved,
1629 lpdwReserved,
1630 lpReserved,
1631 dwFlags);
1633 if ((lpszReserved != NULL) ||
1634 (lpdwReserved != NULL) ||
1635 (lpReserved != NULL))
1637 ERR("Reserved value was not 0\n");
1638 SetLastError(ERROR_INVALID_PARAMETER);
1639 return FALSE;
1641 if (dwFlags != 0)
1642 FIXME("Undocumented flag(s): %x\n", dwFlags);
1643 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1646 /***********************************************************************
1647 * SetUrlCacheEntryInfoA (WININET.@)
1649 BOOL WINAPI SetUrlCacheEntryInfoA(
1650 LPCSTR lpszUrlName,
1651 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1652 DWORD dwFieldControl)
1654 LPURLCACHE_HEADER pHeader;
1655 struct _HASH_ENTRY * pHashEntry;
1656 CACHEFILE_ENTRY * pEntry;
1657 URLCACHECONTAINER * pContainer;
1659 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1661 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1662 return FALSE;
1664 if (!URLCacheContainer_OpenIndex(pContainer))
1665 return FALSE;
1667 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1668 return FALSE;
1670 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1672 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1673 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1674 SetLastError(ERROR_FILE_NOT_FOUND);
1675 return FALSE;
1678 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1679 if (pEntry->dwSignature != URL_SIGNATURE)
1681 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1682 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1683 SetLastError(ERROR_FILE_NOT_FOUND);
1684 return FALSE;
1687 URLCache_SetEntryInfo(
1688 (URL_CACHEFILE_ENTRY *)pEntry,
1689 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1690 dwFieldControl);
1692 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1694 return TRUE;
1697 /***********************************************************************
1698 * SetUrlCacheEntryInfoW (WININET.@)
1700 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1702 LPURLCACHE_HEADER pHeader;
1703 struct _HASH_ENTRY * pHashEntry;
1704 CACHEFILE_ENTRY * pEntry;
1705 URLCACHECONTAINER * pContainer;
1707 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1709 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1710 return FALSE;
1712 if (!URLCacheContainer_OpenIndex(pContainer))
1713 return FALSE;
1715 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1716 return FALSE;
1718 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1720 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1721 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1722 SetLastError(ERROR_FILE_NOT_FOUND);
1723 return FALSE;
1726 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1727 if (pEntry->dwSignature != URL_SIGNATURE)
1729 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1730 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1731 SetLastError(ERROR_FILE_NOT_FOUND);
1732 return FALSE;
1735 URLCache_SetEntryInfo(
1736 (URL_CACHEFILE_ENTRY *)pEntry,
1737 lpCacheEntryInfo,
1738 dwFieldControl);
1740 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1742 return TRUE;
1745 /***********************************************************************
1746 * RetrieveUrlCacheEntryFileA (WININET.@)
1749 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1750 IN LPCSTR lpszUrlName,
1751 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1752 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1753 IN DWORD dwReserved
1756 LPURLCACHE_HEADER pHeader;
1757 struct _HASH_ENTRY * pHashEntry;
1758 CACHEFILE_ENTRY * pEntry;
1759 URL_CACHEFILE_ENTRY * pUrlEntry;
1760 URLCACHECONTAINER * pContainer;
1762 TRACE("(%s, %p, %p, 0x%08x)\n",
1763 debugstr_a(lpszUrlName),
1764 lpCacheEntryInfo,
1765 lpdwCacheEntryInfoBufferSize,
1766 dwReserved);
1768 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1769 return FALSE;
1771 if (!URLCacheContainer_OpenIndex(pContainer))
1772 return FALSE;
1774 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1775 return FALSE;
1777 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1779 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1780 TRACE("entry %s not found!\n", lpszUrlName);
1781 SetLastError(ERROR_FILE_NOT_FOUND);
1782 return FALSE;
1785 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1786 if (pEntry->dwSignature != URL_SIGNATURE)
1788 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1789 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1790 SetLastError(ERROR_FILE_NOT_FOUND);
1791 return FALSE;
1794 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1795 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1796 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1798 pUrlEntry->dwHitRate++;
1799 pUrlEntry->dwUseCount++;
1800 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1802 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1804 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1805 return FALSE;
1807 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1809 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1811 return TRUE;
1814 /***********************************************************************
1815 * RetrieveUrlCacheEntryFileW (WININET.@)
1818 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1819 IN LPCWSTR lpszUrlName,
1820 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1821 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1822 IN DWORD dwReserved
1825 LPURLCACHE_HEADER pHeader;
1826 struct _HASH_ENTRY * pHashEntry;
1827 CACHEFILE_ENTRY * pEntry;
1828 URL_CACHEFILE_ENTRY * pUrlEntry;
1829 URLCACHECONTAINER * pContainer;
1831 TRACE("(%s, %p, %p, 0x%08x)\n",
1832 debugstr_w(lpszUrlName),
1833 lpCacheEntryInfo,
1834 lpdwCacheEntryInfoBufferSize,
1835 dwReserved);
1837 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1838 return FALSE;
1840 if (!URLCacheContainer_OpenIndex(pContainer))
1841 return FALSE;
1843 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1844 return FALSE;
1846 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1848 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1849 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1850 SetLastError(ERROR_FILE_NOT_FOUND);
1851 return FALSE;
1854 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1855 if (pEntry->dwSignature != URL_SIGNATURE)
1857 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1858 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1859 SetLastError(ERROR_FILE_NOT_FOUND);
1860 return FALSE;
1863 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1864 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1865 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1867 pUrlEntry->dwHitRate++;
1868 pUrlEntry->dwUseCount++;
1869 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1871 if (!URLCache_CopyEntry(
1872 pContainer,
1873 pHeader,
1874 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1875 lpdwCacheEntryInfoBufferSize,
1876 pUrlEntry,
1877 TRUE /* UNICODE */))
1879 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1880 return FALSE;
1882 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1884 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1886 return TRUE;
1889 /***********************************************************************
1890 * UnlockUrlCacheEntryFileA (WININET.@)
1893 BOOL WINAPI UnlockUrlCacheEntryFileA(
1894 IN LPCSTR lpszUrlName,
1895 IN DWORD dwReserved
1898 LPURLCACHE_HEADER pHeader;
1899 struct _HASH_ENTRY * pHashEntry;
1900 CACHEFILE_ENTRY * pEntry;
1901 URL_CACHEFILE_ENTRY * pUrlEntry;
1902 URLCACHECONTAINER * pContainer;
1904 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1906 if (dwReserved)
1908 ERR("dwReserved != 0\n");
1909 SetLastError(ERROR_INVALID_PARAMETER);
1910 return FALSE;
1913 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1914 return FALSE;
1916 if (!URLCacheContainer_OpenIndex(pContainer))
1917 return FALSE;
1919 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1920 return FALSE;
1922 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1924 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1925 TRACE("entry %s not found!\n", lpszUrlName);
1926 SetLastError(ERROR_FILE_NOT_FOUND);
1927 return FALSE;
1930 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1931 if (pEntry->dwSignature != URL_SIGNATURE)
1933 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1934 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1935 SetLastError(ERROR_FILE_NOT_FOUND);
1936 return FALSE;
1939 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1941 if (pUrlEntry->dwUseCount == 0)
1943 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1944 return FALSE;
1946 pUrlEntry->dwUseCount--;
1947 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1949 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1951 return TRUE;
1954 /***********************************************************************
1955 * UnlockUrlCacheEntryFileW (WININET.@)
1958 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1960 LPURLCACHE_HEADER pHeader;
1961 struct _HASH_ENTRY * pHashEntry;
1962 CACHEFILE_ENTRY * pEntry;
1963 URL_CACHEFILE_ENTRY * pUrlEntry;
1964 URLCACHECONTAINER * pContainer;
1966 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1968 if (dwReserved)
1970 ERR("dwReserved != 0\n");
1971 SetLastError(ERROR_INVALID_PARAMETER);
1972 return FALSE;
1975 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1976 return FALSE;
1978 if (!URLCacheContainer_OpenIndex(pContainer))
1979 return FALSE;
1981 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1982 return FALSE;
1984 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1986 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1987 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1988 SetLastError(ERROR_FILE_NOT_FOUND);
1989 return FALSE;
1992 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1993 if (pEntry->dwSignature != URL_SIGNATURE)
1995 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1996 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1997 SetLastError(ERROR_FILE_NOT_FOUND);
1998 return FALSE;
2001 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2003 if (pUrlEntry->dwUseCount == 0)
2005 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2006 return FALSE;
2008 pUrlEntry->dwUseCount--;
2009 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2011 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2013 return TRUE;
2016 /***********************************************************************
2017 * CreateUrlCacheEntryA (WININET.@)
2020 BOOL WINAPI CreateUrlCacheEntryA(
2021 IN LPCSTR lpszUrlName,
2022 IN DWORD dwExpectedFileSize,
2023 IN LPCSTR lpszFileExtension,
2024 OUT LPSTR lpszFileName,
2025 IN DWORD dwReserved
2028 DWORD len;
2029 WCHAR *url_name;
2030 WCHAR *file_extension;
2031 WCHAR file_name[MAX_PATH];
2032 BOOL bSuccess = FALSE;
2033 DWORD dwError = 0;
2035 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2036 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2038 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2039 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2040 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2042 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2043 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2045 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2047 bSuccess = TRUE;
2049 else
2051 dwError = GetLastError();
2054 else
2056 dwError = GetLastError();
2058 HeapFree(GetProcessHeap(), 0, file_extension);
2060 else
2062 dwError = GetLastError();
2064 HeapFree(GetProcessHeap(), 0, url_name);
2065 if (!bSuccess)
2066 SetLastError(dwError);
2068 return bSuccess;
2070 /***********************************************************************
2071 * CreateUrlCacheEntryW (WININET.@)
2074 BOOL WINAPI CreateUrlCacheEntryW(
2075 IN LPCWSTR lpszUrlName,
2076 IN DWORD dwExpectedFileSize,
2077 IN LPCWSTR lpszFileExtension,
2078 OUT LPWSTR lpszFileName,
2079 IN DWORD dwReserved
2082 URLCACHECONTAINER * pContainer;
2083 LPURLCACHE_HEADER pHeader;
2084 CHAR szFile[MAX_PATH];
2085 WCHAR szExtension[MAX_PATH];
2086 LPCWSTR lpszUrlPart;
2087 LPCWSTR lpszUrlEnd;
2088 LPCWSTR lpszFileNameExtension;
2089 LPWSTR lpszFileNameNoPath;
2090 int i;
2091 int countnoextension;
2092 BYTE CacheDir;
2093 LONG lBufferSize;
2094 BOOL bFound = FALSE;
2095 int count;
2096 static const WCHAR szWWW[] = {'w','w','w',0};
2098 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2099 debugstr_w(lpszUrlName),
2100 dwExpectedFileSize,
2101 debugstr_w(lpszFileExtension),
2102 lpszFileName,
2103 dwReserved);
2105 if (dwReserved)
2107 ERR("dwReserved != 0\n");
2108 SetLastError(ERROR_INVALID_PARAMETER);
2109 return FALSE;
2112 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2114 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2115 lpszUrlEnd--;
2117 for (lpszUrlPart = lpszUrlEnd;
2118 (lpszUrlPart >= lpszUrlName);
2119 lpszUrlPart--)
2121 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2123 bFound = TRUE;
2124 lpszUrlPart++;
2125 break;
2127 else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2129 lpszUrlEnd = lpszUrlPart;
2132 if (!lstrcmpW(lpszUrlPart, szWWW))
2134 lpszUrlPart += lstrlenW(szWWW);
2137 count = lpszUrlEnd - lpszUrlPart;
2139 if (bFound && (count < MAX_PATH))
2141 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2142 if (!len)
2143 return FALSE;
2144 szFile[len] = '\0';
2145 /* FIXME: get rid of illegal characters like \, / and : */
2147 else
2149 FIXME("need to generate a random filename\n");
2152 TRACE("File name: %s\n", debugstr_a(szFile));
2154 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2155 return FALSE;
2157 if (!URLCacheContainer_OpenIndex(pContainer))
2158 return FALSE;
2160 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2161 return FALSE;
2163 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2165 lBufferSize = MAX_PATH * sizeof(WCHAR);
2166 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2168 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2170 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2171 lpszFileNameNoPath >= lpszFileName;
2172 --lpszFileNameNoPath)
2174 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2175 break;
2178 countnoextension = lstrlenW(lpszFileNameNoPath);
2179 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2180 if (lpszFileNameExtension)
2181 countnoextension -= lstrlenW(lpszFileNameExtension);
2182 *szExtension = '\0';
2184 if (lpszFileExtension)
2186 szExtension[0] = '.';
2187 lstrcpyW(szExtension+1, lpszFileExtension);
2190 for (i = 0; i < 255; i++)
2192 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2193 HANDLE hFile;
2194 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2195 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2196 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2197 if (hFile != INVALID_HANDLE_VALUE)
2199 CloseHandle(hFile);
2200 return TRUE;
2204 return FALSE;
2208 /***********************************************************************
2209 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2211 * The bug we are compensating for is that some drongo at Microsoft
2212 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2213 * As a consequence, CommitUrlCacheEntryA has been effectively
2214 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2215 * is still defined as LPCWSTR. The result (other than madness) is
2216 * that we always need to store lpHeaderInfo in CP_ACP rather than
2217 * in UTF16, and we need to avoid converting lpHeaderInfo in
2218 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2219 * result will lose data for arbitrary binary data.
2222 static BOOL WINAPI CommitUrlCacheEntryInternal(
2223 IN LPCWSTR lpszUrlName,
2224 IN LPCWSTR lpszLocalFileName,
2225 IN FILETIME ExpireTime,
2226 IN FILETIME LastModifiedTime,
2227 IN DWORD CacheEntryType,
2228 IN LPBYTE lpHeaderInfo,
2229 IN DWORD dwHeaderSize,
2230 IN LPCWSTR lpszFileExtension,
2231 IN LPCWSTR lpszOriginalUrl
2234 URLCACHECONTAINER * pContainer;
2235 LPURLCACHE_HEADER pHeader;
2236 struct _HASH_ENTRY * pHashEntry;
2237 CACHEFILE_ENTRY * pEntry;
2238 URL_CACHEFILE_ENTRY * pUrlEntry;
2239 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2240 DWORD dwOffsetLocalFileName = 0;
2241 DWORD dwOffsetHeader = 0;
2242 DWORD dwOffsetFileExtension = 0;
2243 DWORD dwFileSizeLow = 0;
2244 DWORD dwFileSizeHigh = 0;
2245 BYTE cDirectory = 0;
2246 int len;
2247 char achFile[MAX_PATH];
2248 LPSTR lpszUrlNameA = NULL;
2249 LPSTR lpszFileExtensionA = NULL;
2250 char *pchLocalFileName = 0;
2251 DWORD error = ERROR_SUCCESS;
2253 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2254 debugstr_w(lpszUrlName),
2255 debugstr_w(lpszLocalFileName),
2256 CacheEntryType,
2257 lpHeaderInfo,
2258 dwHeaderSize,
2259 debugstr_w(lpszFileExtension),
2260 debugstr_w(lpszOriginalUrl));
2262 if (lpszOriginalUrl)
2263 WARN(": lpszOriginalUrl ignored\n");
2265 if (lpszLocalFileName)
2267 HANDLE hFile;
2269 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2270 if (hFile == INVALID_HANDLE_VALUE)
2272 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2273 return FALSE;
2276 /* Get file size */
2277 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2278 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2280 ERR("couldn't get file size (error is %d)\n", GetLastError());
2281 CloseHandle(hFile);
2282 return FALSE;
2285 CloseHandle(hFile);
2288 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2289 return FALSE;
2291 if (!URLCacheContainer_OpenIndex(pContainer))
2292 return FALSE;
2294 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2295 return FALSE;
2297 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2298 lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2299 if (!lpszUrlNameA)
2301 error = GetLastError();
2302 goto cleanup;
2304 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2306 if (lpszFileExtension)
2308 len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2309 lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2310 if (!lpszFileExtensionA)
2312 error = GetLastError();
2313 goto cleanup;
2315 WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2318 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2320 FIXME("entry already in cache - don't know what to do!\n");
2322 * SetLastError(ERROR_FILE_NOT_FOUND);
2323 * return FALSE;
2325 goto cleanup;
2328 if (lpszLocalFileName)
2330 BOOL bFound = FALSE;
2332 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2334 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2335 error = ERROR_INVALID_PARAMETER;
2336 goto cleanup;
2339 /* skip container path prefix */
2340 lpszLocalFileName += lstrlenW(pContainer->path);
2342 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2343 pchLocalFileName = achFile;
2345 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2347 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2349 bFound = TRUE;
2350 break;
2354 if (!bFound)
2356 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2357 error = ERROR_INVALID_PARAMETER;
2358 goto cleanup;
2361 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2364 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2365 if (lpszLocalFileName)
2367 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2368 dwOffsetLocalFileName = dwBytesNeeded;
2369 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2371 if (lpHeaderInfo)
2373 dwOffsetHeader = dwBytesNeeded;
2374 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2376 if (lpszFileExtensionA)
2378 dwOffsetFileExtension = dwBytesNeeded;
2379 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2382 /* round up to next block */
2383 if (dwBytesNeeded % BLOCKSIZE)
2385 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2386 dwBytesNeeded += BLOCKSIZE;
2389 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2391 ERR("no free entries\n");
2392 error = ERROR_DISK_FULL;
2393 goto cleanup;
2396 /* FindFirstFreeEntry fills in blocks used */
2397 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2398 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2399 pUrlEntry->CacheDir = cDirectory;
2400 pUrlEntry->CacheEntryType = CacheEntryType;
2401 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2402 pUrlEntry->dwExemptDelta = 0;
2403 pUrlEntry->dwHitRate = 0;
2404 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2405 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2406 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2407 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2408 pUrlEntry->dwSizeHigh = 0;
2409 pUrlEntry->dwSizeLow = dwFileSizeLow;
2410 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2411 pUrlEntry->dwUseCount = 0;
2412 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2413 pUrlEntry->LastModifiedTime = LastModifiedTime;
2414 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2415 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2416 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2417 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2419 /*** Unknowns ***/
2420 pUrlEntry->dwUnknown1 = 0;
2421 pUrlEntry->dwUnknown2 = 0;
2422 pUrlEntry->dwUnknown3 = 0x60;
2423 pUrlEntry->Unknown4 = 0;
2424 pUrlEntry->wUnknown5 = 0x1010;
2425 pUrlEntry->dwUnknown7 = 0;
2426 pUrlEntry->dwUnknown8 = 0;
2429 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2430 if (dwOffsetLocalFileName)
2431 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2432 if (dwOffsetHeader)
2433 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2434 if (dwOffsetFileExtension)
2435 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2437 if (!URLCache_AddEntryToHash(pHeader, lpszUrlNameA, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2439 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2440 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2441 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2442 return FALSE;
2445 cleanup:
2446 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2447 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2448 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2450 if (error == ERROR_SUCCESS)
2451 return TRUE;
2452 else
2454 SetLastError(error);
2455 return FALSE;
2459 /***********************************************************************
2460 * CommitUrlCacheEntryA (WININET.@)
2463 BOOL WINAPI CommitUrlCacheEntryA(
2464 IN LPCSTR lpszUrlName,
2465 IN LPCSTR lpszLocalFileName,
2466 IN FILETIME ExpireTime,
2467 IN FILETIME LastModifiedTime,
2468 IN DWORD CacheEntryType,
2469 IN LPBYTE lpHeaderInfo,
2470 IN DWORD dwHeaderSize,
2471 IN LPCSTR lpszFileExtension,
2472 IN LPCSTR lpszOriginalUrl
2475 DWORD len;
2476 WCHAR *url_name = NULL;
2477 WCHAR *local_file_name = NULL;
2478 WCHAR *original_url = NULL;
2479 WCHAR *file_extension = NULL;
2480 BOOL bSuccess = FALSE;
2482 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2483 debugstr_a(lpszUrlName),
2484 debugstr_a(lpszLocalFileName),
2485 CacheEntryType,
2486 lpHeaderInfo,
2487 dwHeaderSize,
2488 debugstr_a(lpszFileExtension),
2489 debugstr_a(lpszOriginalUrl));
2491 len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2492 url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2493 if (!url_name)
2494 goto cleanup;
2495 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2497 if (lpszLocalFileName)
2499 len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2500 local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2501 if (!local_file_name)
2502 goto cleanup;
2503 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2505 if (lpszFileExtension)
2507 len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2508 file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2509 if (!file_extension)
2510 goto cleanup;
2511 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2513 if (lpszOriginalUrl)
2515 len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2516 original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2517 if (!original_url)
2518 goto cleanup;
2519 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2522 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2523 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2524 file_extension, original_url);
2526 cleanup:
2527 HeapFree(GetProcessHeap(), 0, original_url);
2528 HeapFree(GetProcessHeap(), 0, file_extension);
2529 HeapFree(GetProcessHeap(), 0, local_file_name);
2530 HeapFree(GetProcessHeap(), 0, url_name);
2532 return bSuccess;
2535 /***********************************************************************
2536 * CommitUrlCacheEntryW (WININET.@)
2539 BOOL WINAPI CommitUrlCacheEntryW(
2540 IN LPCWSTR lpszUrlName,
2541 IN LPCWSTR lpszLocalFileName,
2542 IN FILETIME ExpireTime,
2543 IN FILETIME LastModifiedTime,
2544 IN DWORD CacheEntryType,
2545 IN LPWSTR lpHeaderInfo,
2546 IN DWORD dwHeaderSize,
2547 IN LPCWSTR lpszFileExtension,
2548 IN LPCWSTR lpszOriginalUrl
2551 DWORD dwError = 0;
2552 BOOL bSuccess = FALSE;
2553 DWORD len = 0;
2554 CHAR *header_info = NULL;
2556 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2557 debugstr_w(lpszUrlName),
2558 debugstr_w(lpszLocalFileName),
2559 CacheEntryType,
2560 lpHeaderInfo,
2561 dwHeaderSize,
2562 debugstr_w(lpszFileExtension),
2563 debugstr_w(lpszOriginalUrl));
2565 if (!lpHeaderInfo ||
2566 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2567 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2569 if (header_info)
2570 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2571 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2572 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2574 bSuccess = TRUE;
2576 else
2578 dwError = GetLastError();
2580 if (header_info)
2582 HeapFree(GetProcessHeap(), 0, header_info);
2583 if (!bSuccess)
2584 SetLastError(dwError);
2587 return bSuccess;
2590 /***********************************************************************
2591 * ReadUrlCacheEntryStream (WININET.@)
2594 BOOL WINAPI ReadUrlCacheEntryStream(
2595 IN HANDLE hUrlCacheStream,
2596 IN DWORD dwLocation,
2597 IN OUT LPVOID lpBuffer,
2598 IN OUT LPDWORD lpdwLen,
2599 IN DWORD dwReserved
2602 /* Get handle to file from 'stream' */
2603 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2605 if (dwReserved != 0)
2607 ERR("dwReserved != 0\n");
2608 SetLastError(ERROR_INVALID_PARAMETER);
2609 return FALSE;
2612 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2614 SetLastError(ERROR_INVALID_HANDLE);
2615 return FALSE;
2618 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2619 return FALSE;
2620 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2623 /***********************************************************************
2624 * RetrieveUrlCacheEntryStreamA (WININET.@)
2627 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2628 IN LPCSTR lpszUrlName,
2629 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2630 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2631 IN BOOL fRandomRead,
2632 IN DWORD dwReserved
2635 /* NOTE: this is not the same as the way that the native
2636 * version allocates 'stream' handles. I did it this way
2637 * as it is much easier and no applications should depend
2638 * on this behaviour. (Native version appears to allocate
2639 * indices into a table)
2641 STREAM_HANDLE * pStream;
2642 HANDLE hFile;
2644 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2645 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2647 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2648 lpCacheEntryInfo,
2649 lpdwCacheEntryInfoBufferSize,
2650 dwReserved))
2652 return NULL;
2655 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2656 GENERIC_READ,
2657 FILE_SHARE_READ,
2658 NULL,
2659 OPEN_EXISTING,
2660 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2661 NULL);
2662 if (hFile == INVALID_HANDLE_VALUE)
2663 return FALSE;
2665 /* allocate handle storage space */
2666 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2667 if (!pStream)
2669 CloseHandle(hFile);
2670 SetLastError(ERROR_OUTOFMEMORY);
2671 return FALSE;
2674 pStream->hFile = hFile;
2675 strcpy(pStream->lpszUrl, lpszUrlName);
2676 return (HANDLE)pStream;
2679 /***********************************************************************
2680 * RetrieveUrlCacheEntryStreamW (WININET.@)
2683 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2684 IN LPCWSTR lpszUrlName,
2685 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2686 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2687 IN BOOL fRandomRead,
2688 IN DWORD dwReserved
2691 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2692 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2693 return NULL;
2696 /***********************************************************************
2697 * UnlockUrlCacheEntryStream (WININET.@)
2700 BOOL WINAPI UnlockUrlCacheEntryStream(
2701 IN HANDLE hUrlCacheStream,
2702 IN DWORD dwReserved
2705 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2707 if (dwReserved != 0)
2709 ERR("dwReserved != 0\n");
2710 SetLastError(ERROR_INVALID_PARAMETER);
2711 return FALSE;
2714 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2716 SetLastError(ERROR_INVALID_HANDLE);
2717 return FALSE;
2720 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2721 return FALSE;
2723 /* close file handle */
2724 CloseHandle(pStream->hFile);
2726 /* free allocated space */
2727 HeapFree(GetProcessHeap(), 0, pStream);
2729 return TRUE;
2733 /***********************************************************************
2734 * DeleteUrlCacheEntryA (WININET.@)
2737 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2739 URLCACHECONTAINER * pContainer;
2740 LPURLCACHE_HEADER pHeader;
2741 struct _HASH_ENTRY * pHashEntry;
2742 CACHEFILE_ENTRY * pEntry;
2744 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2746 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2747 return FALSE;
2749 if (!URLCacheContainer_OpenIndex(pContainer))
2750 return FALSE;
2752 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2753 return FALSE;
2755 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2757 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2758 TRACE("entry %s not found!\n", lpszUrlName);
2759 SetLastError(ERROR_FILE_NOT_FOUND);
2760 return FALSE;
2763 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2764 URLCache_DeleteEntry(pHeader, pEntry);
2766 URLCache_DeleteEntryFromHash(pHashEntry);
2768 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2770 return TRUE;
2773 /***********************************************************************
2774 * DeleteUrlCacheEntryW (WININET.@)
2777 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2779 URLCACHECONTAINER * pContainer;
2780 LPURLCACHE_HEADER pHeader;
2781 struct _HASH_ENTRY * pHashEntry;
2782 CACHEFILE_ENTRY * pEntry;
2783 LPSTR urlA;
2784 int url_len;
2786 TRACE("(%s)\n", debugstr_w(lpszUrlName));
2788 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2789 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2790 if (!urlA)
2792 SetLastError(ERROR_OUTOFMEMORY);
2793 return FALSE;
2795 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2797 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2799 HeapFree(GetProcessHeap(), 0, urlA);
2800 return FALSE;
2802 if (!URLCacheContainer_OpenIndex(pContainer))
2804 HeapFree(GetProcessHeap(), 0, urlA);
2805 return FALSE;
2807 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2809 HeapFree(GetProcessHeap(), 0, urlA);
2810 return FALSE;
2813 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2815 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2816 TRACE("entry %s not found!\n", debugstr_a(urlA));
2817 HeapFree(GetProcessHeap(), 0, urlA);
2818 SetLastError(ERROR_FILE_NOT_FOUND);
2819 return FALSE;
2822 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2823 URLCache_DeleteEntry(pHeader, pEntry);
2825 URLCache_DeleteEntryFromHash(pHashEntry);
2827 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2829 HeapFree(GetProcessHeap(), 0, urlA);
2830 return TRUE;
2833 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2835 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2836 return TRUE;
2839 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2841 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2842 return TRUE;
2845 /***********************************************************************
2846 * CreateCacheContainerA (WININET.@)
2848 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2849 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2851 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2852 d1, d2, d3, d4, d5, d6, d7, d8);
2853 return TRUE;
2856 /***********************************************************************
2857 * CreateCacheContainerW (WININET.@)
2859 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2860 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2862 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2863 d1, d2, d3, d4, d5, d6, d7, d8);
2864 return TRUE;
2867 /***********************************************************************
2868 * FindFirstUrlCacheContainerA (WININET.@)
2870 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2872 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2873 return NULL;
2876 /***********************************************************************
2877 * FindFirstUrlCacheContainerW (WININET.@)
2879 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2881 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2882 return NULL;
2885 /***********************************************************************
2886 * FindNextUrlCacheContainerA (WININET.@)
2888 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2890 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2891 return FALSE;
2894 /***********************************************************************
2895 * FindNextUrlCacheContainerW (WININET.@)
2897 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2899 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2900 return FALSE;
2903 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2904 LPCSTR lpszUrlSearchPattern,
2905 DWORD dwFlags,
2906 DWORD dwFilter,
2907 GROUPID GroupId,
2908 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2909 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2910 LPVOID lpReserved,
2911 LPDWORD pcbReserved2,
2912 LPVOID lpReserved3
2915 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2916 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2917 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2918 SetLastError(ERROR_FILE_NOT_FOUND);
2919 return NULL;
2922 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2923 LPCWSTR lpszUrlSearchPattern,
2924 DWORD dwFlags,
2925 DWORD dwFilter,
2926 GROUPID GroupId,
2927 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2928 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2929 LPVOID lpReserved,
2930 LPDWORD pcbReserved2,
2931 LPVOID lpReserved3
2934 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2935 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2936 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2937 SetLastError(ERROR_FILE_NOT_FOUND);
2938 return NULL;
2941 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
2943 typedef struct URLCacheFindEntryHandle
2945 DWORD dwMagic;
2946 LPWSTR lpszUrlSearchPattern;
2947 DWORD dwContainerIndex;
2948 DWORD dwHashTableIndex;
2949 DWORD dwHashEntryIndex;
2950 } URLCacheFindEntryHandle;
2952 /***********************************************************************
2953 * FindFirstUrlCacheEntryA (WININET.@)
2956 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2957 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2959 URLCacheFindEntryHandle *pEntryHandle;
2961 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2963 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
2964 if (!pEntryHandle)
2965 return NULL;
2967 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
2968 if (lpszUrlSearchPattern)
2970 int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
2971 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2972 if (!pEntryHandle->lpszUrlSearchPattern)
2974 HeapFree(GetProcessHeap(), 0, pEntryHandle);
2975 return NULL;
2977 MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
2979 else
2980 pEntryHandle->lpszUrlSearchPattern = NULL;
2981 pEntryHandle->dwContainerIndex = 0;
2982 pEntryHandle->dwHashTableIndex = 0;
2983 pEntryHandle->dwHashEntryIndex = 0;
2985 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
2987 HeapFree(GetProcessHeap(), 0, pEntryHandle);
2988 return NULL;
2990 return pEntryHandle;
2993 /***********************************************************************
2994 * FindFirstUrlCacheEntryW (WININET.@)
2997 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2998 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3000 URLCacheFindEntryHandle *pEntryHandle;
3002 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3004 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3005 if (!pEntryHandle)
3006 return NULL;
3008 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3009 if (lpszUrlSearchPattern)
3011 int len = strlenW(lpszUrlSearchPattern);
3012 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3013 if (!pEntryHandle->lpszUrlSearchPattern)
3015 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3016 return NULL;
3018 memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3020 else
3021 pEntryHandle->lpszUrlSearchPattern = NULL;
3022 pEntryHandle->dwContainerIndex = 0;
3023 pEntryHandle->dwHashTableIndex = 0;
3024 pEntryHandle->dwHashEntryIndex = 0;
3026 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3028 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3029 return NULL;
3031 return pEntryHandle;
3034 /***********************************************************************
3035 * FindNextUrlCacheEntryA (WININET.@)
3037 BOOL WINAPI FindNextUrlCacheEntryA(
3038 HANDLE hEnumHandle,
3039 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3040 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3042 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3043 URLCACHECONTAINER * pContainer;
3045 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3047 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3049 SetLastError(ERROR_INVALID_HANDLE);
3050 return FALSE;
3053 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3054 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3056 LPURLCACHE_HEADER pHeader;
3057 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3059 if (!URLCacheContainer_OpenIndex(pContainer))
3060 return FALSE;
3062 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3063 return FALSE;
3065 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3066 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3068 const struct _HASH_ENTRY *pHashEntry = NULL;
3069 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3070 pEntryHandle->dwHashEntryIndex++)
3072 const URL_CACHEFILE_ENTRY *pUrlEntry;
3073 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3075 if (pEntry->dwSignature != URL_SIGNATURE)
3076 continue;
3078 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3079 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3080 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3082 if (!URLCache_CopyEntry(
3083 pContainer,
3084 pHeader,
3085 lpNextCacheEntryInfo,
3086 lpdwNextCacheEntryInfoBufferSize,
3087 pUrlEntry,
3088 FALSE /* not UNICODE */))
3090 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3091 return FALSE;
3093 TRACE("Local File Name: %s\n", debugstr_a(lpNextCacheEntryInfo->lpszLocalFileName));
3095 /* increment the current index so that next time the function
3096 * is called the next entry is returned */
3097 pEntryHandle->dwHashEntryIndex++;
3098 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3099 return TRUE;
3103 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3106 SetLastError(ERROR_NO_MORE_ITEMS);
3107 return FALSE;
3110 BOOL WINAPI FindNextUrlCacheEntryW(
3111 HANDLE hEnumHandle,
3112 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3113 LPDWORD lpdwNextCacheEntryInfoBufferSize
3116 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3117 return FALSE;
3120 /***********************************************************************
3121 * FindCloseUrlCache (WININET.@)
3123 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3125 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3127 TRACE("(%p)\n", hEnumHandle);
3129 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3131 SetLastError(ERROR_INVALID_HANDLE);
3132 return FALSE;
3135 pEntryHandle->dwMagic = 0;
3136 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3137 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3139 return TRUE;
3142 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3143 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3145 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3146 dwSearchCondition, lpGroupId, lpReserved);
3147 return NULL;
3150 BOOL WINAPI FindNextUrlCacheEntryExA(
3151 HANDLE hEnumHandle,
3152 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3153 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3154 LPVOID lpReserved,
3155 LPDWORD pcbReserved2,
3156 LPVOID lpReserved3
3159 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3160 lpReserved, pcbReserved2, lpReserved3);
3161 return FALSE;
3164 BOOL WINAPI FindNextUrlCacheEntryExW(
3165 HANDLE hEnumHandle,
3166 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3167 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3168 LPVOID lpReserved,
3169 LPDWORD pcbReserved2,
3170 LPVOID lpReserved3
3173 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3174 lpReserved, pcbReserved2, lpReserved3);
3175 return FALSE;
3178 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3180 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3181 return FALSE;
3184 /***********************************************************************
3185 * CreateUrlCacheGroup (WININET.@)
3188 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3190 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3191 return FALSE;
3194 /***********************************************************************
3195 * DeleteUrlCacheGroup (WININET.@)
3198 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3200 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3201 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3202 return FALSE;
3205 /***********************************************************************
3206 * SetUrlCacheEntryGroupA (WININET.@)
3209 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3210 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3211 LPVOID lpReserved)
3213 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3214 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3215 pbGroupAttributes, cbGroupAttributes, lpReserved);
3216 SetLastError(ERROR_FILE_NOT_FOUND);
3217 return FALSE;
3220 /***********************************************************************
3221 * SetUrlCacheEntryGroupW (WININET.@)
3224 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3225 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3226 LPVOID lpReserved)
3228 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3229 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3230 pbGroupAttributes, cbGroupAttributes, lpReserved);
3231 SetLastError(ERROR_FILE_NOT_FOUND);
3232 return FALSE;
3235 /***********************************************************************
3236 * GetUrlCacheConfigInfoW (WININET.@)
3238 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3240 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3241 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3242 return FALSE;
3245 /***********************************************************************
3246 * GetUrlCacheConfigInfoA (WININET.@)
3248 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3250 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3252 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3253 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3254 return FALSE;
3257 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3258 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3259 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3261 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3262 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3263 lpdwGroupInfo, lpReserved);
3264 return FALSE;
3267 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3268 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3269 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3271 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3272 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3273 lpdwGroupInfo, lpReserved);
3274 return FALSE;
3277 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3278 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3280 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3281 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3282 return TRUE;
3285 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3286 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3288 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3289 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3290 return TRUE;
3293 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3295 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3296 return TRUE;
3299 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3301 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3302 return TRUE;
3305 /***********************************************************************
3306 * DeleteIE3Cache (WININET.@)
3308 * Deletes the files used by the IE3 URL caching system.
3310 * PARAMS
3311 * hWnd [I] A dummy window.
3312 * hInst [I] Instance of process calling the function.
3313 * lpszCmdLine [I] Options used by function.
3314 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3316 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3318 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3319 return 0;
3322 /***********************************************************************
3323 * IsUrlCacheEntryExpiredA (WININET.@)
3325 * PARAMS
3326 * url [I] Url
3327 * dwFlags [I] Unknown
3328 * pftLastModified [O] Last modified time
3330 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3332 LPURLCACHE_HEADER pHeader;
3333 struct _HASH_ENTRY * pHashEntry;
3334 const CACHEFILE_ENTRY * pEntry;
3335 const URL_CACHEFILE_ENTRY * pUrlEntry;
3336 URLCACHECONTAINER * pContainer;
3338 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3340 if (!URLCacheContainers_FindContainerA(url, &pContainer))
3341 return FALSE;
3343 if (!URLCacheContainer_OpenIndex(pContainer))
3344 return FALSE;
3346 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3347 return FALSE;
3349 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3351 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3352 TRACE("entry %s not found!\n", url);
3353 SetLastError(ERROR_FILE_NOT_FOUND);
3354 return FALSE;
3357 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3358 if (pEntry->dwSignature != URL_SIGNATURE)
3360 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3361 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3362 SetLastError(ERROR_FILE_NOT_FOUND);
3363 return FALSE;
3366 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3368 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3370 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3372 return TRUE;
3375 /***********************************************************************
3376 * IsUrlCacheEntryExpiredW (WININET.@)
3378 * PARAMS
3379 * url [I] Url
3380 * dwFlags [I] Unknown
3381 * pftLastModified [O] Last modified time
3383 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3385 LPURLCACHE_HEADER pHeader;
3386 struct _HASH_ENTRY * pHashEntry;
3387 const CACHEFILE_ENTRY * pEntry;
3388 const URL_CACHEFILE_ENTRY * pUrlEntry;
3389 URLCACHECONTAINER * pContainer;
3391 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3393 if (!URLCacheContainers_FindContainerW(url, &pContainer))
3394 return FALSE;
3396 if (!URLCacheContainer_OpenIndex(pContainer))
3397 return FALSE;
3399 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3400 return FALSE;
3402 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3404 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3405 TRACE("entry %s not found!\n", debugstr_w(url));
3406 SetLastError(ERROR_FILE_NOT_FOUND);
3407 return FALSE;
3410 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3411 if (pEntry->dwSignature != URL_SIGNATURE)
3413 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3414 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3415 SetLastError(ERROR_FILE_NOT_FOUND);
3416 return FALSE;
3419 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3421 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3423 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3425 return TRUE;