wininet: The URL in CommitUrlCacheEntryInternal shouldn't be restricted to MAX_PATH...
[wine/wine-kai.git] / dlls / wininet / urlcache.c
blob4ef881f3252ea549b57060a4b1f3741f7e5b0797
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 dwUnknown6; /* usually zero */
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 int k;
380 /* The following is OK because we generated an
381 * 8 character directory name made from characters
382 * [A-Z0-9], which are equivalent for all code
383 * pages and for UTF-16
385 for (k = 0; k < 8; ++k)
386 pHeader->directory_data[i].filename[k] = pwchDir[k];
387 break;
389 else if (j >= 255)
391 /* Give up. The most likely cause of this
392 * is a full disk, but whatever the cause
393 * is, it should be more than apparent that
394 * we won't succeed.
396 dwError = GetLastError();
397 break;
402 UnmapViewOfFile(pHeader);
404 else
406 dwError = GetLastError();
408 CloseHandle(hMapping);
410 else
412 dwError = GetLastError();
416 if (dwError)
418 CloseHandle(hFile);
419 DeleteFileW(wszFilePath);
420 ReleaseMutex(pContainer->hMutex);
421 SetLastError(dwError);
422 return FALSE;
427 ReleaseMutex(pContainer->hMutex);
429 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
430 URLCache_PathToObjectName(wszFilePath, '_');
431 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
432 if (!pContainer->hMapping)
433 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
434 CloseHandle(hFile);
435 if (!pContainer->hMapping)
437 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
438 return FALSE;
441 return TRUE;
444 /***********************************************************************
445 * URLCacheContainer_CloseIndex (Internal)
447 * Closes the index
449 * RETURNS
450 * nothing
453 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
455 CloseHandle(pContainer->hMapping);
456 pContainer->hMapping = NULL;
459 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
461 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
462 int path_len = strlenW(path);
463 int cache_prefix_len = strlenW(cache_prefix);
465 if (!pContainer)
467 return FALSE;
470 pContainer->hMapping = NULL;
471 pContainer->file_size = 0;
473 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
474 if (!pContainer->path)
476 HeapFree(GetProcessHeap(), 0, pContainer);
477 return FALSE;
480 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
482 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
483 if (!pContainer->cache_prefix)
485 HeapFree(GetProcessHeap(), 0, pContainer->path);
486 HeapFree(GetProcessHeap(), 0, pContainer);
487 return FALSE;
490 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
492 CharLowerW(mutex_name);
493 URLCache_PathToObjectName(mutex_name, '!');
495 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
497 ERR("couldn't create mutex (error is %d)\n", GetLastError());
498 HeapFree(GetProcessHeap(), 0, pContainer->path);
499 HeapFree(GetProcessHeap(), 0, pContainer);
500 return FALSE;
503 list_add_head(&UrlContainers, &pContainer->entry);
505 return TRUE;
508 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
510 list_remove(&pContainer->entry);
512 URLCacheContainer_CloseIndex(pContainer);
513 CloseHandle(pContainer->hMutex);
514 HeapFree(GetProcessHeap(), 0, pContainer->path);
515 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
516 HeapFree(GetProcessHeap(), 0, pContainer);
519 void URLCacheContainers_CreateDefaults(void)
521 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
522 static const WCHAR UrlPrefix[] = {0};
523 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
524 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
525 static const WCHAR CookieSuffix[] = {0};
526 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
527 static const struct
529 int nFolder; /* CSIDL_* constant */
530 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
531 const WCHAR * cache_prefix; /* prefix used to reference the container */
532 } DefaultContainerData[] =
534 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
535 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
536 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
538 DWORD i;
540 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
542 WCHAR wszCachePath[MAX_PATH];
543 WCHAR wszMutexName[MAX_PATH];
544 int path_len, suffix_len;
546 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
548 ERR("Couldn't get path for default container %u\n", i);
549 continue;
551 path_len = strlenW(wszCachePath);
552 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
554 if (path_len + suffix_len + 2 > MAX_PATH)
556 ERR("Path too long\n");
557 continue;
560 wszCachePath[path_len] = '\\';
561 wszCachePath[path_len+1] = 0;
563 strcpyW(wszMutexName, wszCachePath);
565 if (suffix_len)
567 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
568 wszCachePath[path_len + suffix_len + 1] = '\\';
569 wszCachePath[path_len + suffix_len + 2] = '\0';
572 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
576 void URLCacheContainers_DeleteAll(void)
578 while(!list_empty(&UrlContainers))
579 URLCacheContainer_DeleteContainer(
580 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
584 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
586 URLCACHECONTAINER * pContainer;
588 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
590 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
592 int prefix_len = strlenW(pContainer->cache_prefix);
593 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
595 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
596 *ppContainer = pContainer;
597 return TRUE;
600 ERR("no container found\n");
601 SetLastError(ERROR_FILE_NOT_FOUND);
602 return FALSE;
605 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
607 BOOL ret;
608 LPWSTR lpwszUrl;
609 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
610 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
612 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
613 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
614 HeapFree(GetProcessHeap(), 0, lpwszUrl);
615 return ret;
617 return FALSE;
620 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
622 DWORD i = 0;
623 URLCACHECONTAINER * pContainer;
625 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
627 /* non-NULL search pattern only returns one container ever */
628 if (lpwszSearchPattern && dwIndex > 0)
629 return FALSE;
631 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
633 if (lpwszSearchPattern)
635 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
637 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
638 *ppContainer = pContainer;
639 return TRUE;
642 else
644 if (i == dwIndex)
646 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
647 *ppContainer = pContainer;
648 return TRUE;
651 i++;
653 return FALSE;
656 /***********************************************************************
657 * URLCacheContainer_LockIndex (Internal)
660 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
662 BYTE index;
663 LPVOID pIndexData;
664 URLCACHE_HEADER * pHeader;
666 /* acquire mutex */
667 WaitForSingleObject(pContainer->hMutex, INFINITE);
669 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
671 if (!pIndexData)
673 ReleaseMutex(pContainer->hMutex);
674 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
675 return FALSE;
677 pHeader = (URLCACHE_HEADER *)pIndexData;
679 /* file has grown - we need to remap to prevent us getting
680 * access violations when we try and access beyond the end
681 * of the memory mapped file */
682 if (pHeader->dwFileSize != pContainer->file_size)
684 URLCacheContainer_CloseIndex(pContainer);
685 if (!URLCacheContainer_OpenIndex(pContainer))
687 ReleaseMutex(pContainer->hMutex);
688 return FALSE;
690 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
692 if (!pIndexData)
694 ReleaseMutex(pContainer->hMutex);
695 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
696 return FALSE;
698 pHeader = (URLCACHE_HEADER *)pIndexData;
701 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
703 for (index = 0; index < pHeader->DirectoryCount; index++)
705 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
708 return pHeader;
711 /***********************************************************************
712 * URLCacheContainer_UnlockIndex (Internal)
715 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
717 /* release mutex */
718 ReleaseMutex(pContainer->hMutex);
719 return UnmapViewOfFile(pHeader);
723 #ifndef CHAR_BIT
724 #define CHAR_BIT (8 * sizeof(CHAR))
725 #endif
727 /***********************************************************************
728 * URLCache_Allocation_BlockIsFree (Internal)
730 * Is the specified block number free?
732 * RETURNS
733 * zero if free
734 * non-zero otherwise
737 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
739 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
740 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
743 /***********************************************************************
744 * URLCache_Allocation_BlockFree (Internal)
746 * Marks the specified block as free
748 * RETURNS
749 * nothing
752 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
754 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
755 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
758 /***********************************************************************
759 * URLCache_Allocation_BlockAllocate (Internal)
761 * Marks the specified block as allocated
763 * RETURNS
764 * nothing
767 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
769 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
770 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
773 /***********************************************************************
774 * URLCache_FindFirstFreeEntry (Internal)
776 * Finds and allocates the first block of free space big enough and
777 * sets ppEntry to point to it.
779 * RETURNS
780 * TRUE if it had enough space
781 * FALSE if it couldn't find enough space
784 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
786 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
787 DWORD dwBlockNumber;
788 DWORD dwFreeCounter;
789 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
791 for (dwFreeCounter = 0;
792 dwFreeCounter < dwBlocksNeeded &&
793 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
794 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
795 dwFreeCounter++)
796 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
798 if (dwFreeCounter == dwBlocksNeeded)
800 DWORD index;
801 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
802 for (index = 0; index < dwBlocksNeeded; index++)
803 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
804 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
805 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
806 return TRUE;
809 FIXME("Grow file\n");
810 return FALSE;
813 /***********************************************************************
814 * URLCache_DeleteEntry (Internal)
816 * Deletes the specified entry and frees the space allocated to it
818 * RETURNS
819 * TRUE if it succeeded
820 * FALSE if it failed
823 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
825 DWORD dwStartBlock;
826 DWORD dwBlock;
827 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
829 /* update allocation table */
830 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
831 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
832 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
834 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
835 return TRUE;
838 /***********************************************************************
839 * URLCache_LocalFileNameToPathW (Internal)
841 * Copies the full path to the specified buffer given the local file
842 * name and the index of the directory it is in. Always sets value in
843 * lpBufferSize to the required buffer size (in bytes).
845 * RETURNS
846 * TRUE if the buffer was big enough
847 * FALSE if the buffer was too small
850 static BOOL URLCache_LocalFileNameToPathW(
851 const URLCACHECONTAINER * pContainer,
852 LPCURLCACHE_HEADER pHeader,
853 LPCSTR szLocalFileName,
854 BYTE Directory,
855 LPWSTR wszPath,
856 LPLONG lpBufferSize)
858 LONG nRequired;
859 int path_len = strlenW(pContainer->path);
860 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
861 if (Directory >= pHeader->DirectoryCount)
863 *lpBufferSize = 0;
864 return FALSE;
867 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
868 if (nRequired < *lpBufferSize)
870 int dir_len;
872 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
873 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
874 wszPath[dir_len + path_len] = '\\';
875 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
876 *lpBufferSize = nRequired;
877 return TRUE;
879 *lpBufferSize = nRequired;
880 return FALSE;
883 /***********************************************************************
884 * URLCache_LocalFileNameToPathA (Internal)
886 * Copies the full path to the specified buffer given the local file
887 * name and the index of the directory it is in. Always sets value in
888 * lpBufferSize to the required buffer size.
890 * RETURNS
891 * TRUE if the buffer was big enough
892 * FALSE if the buffer was too small
895 static BOOL URLCache_LocalFileNameToPathA(
896 const URLCACHECONTAINER * pContainer,
897 LPCURLCACHE_HEADER pHeader,
898 LPCSTR szLocalFileName,
899 BYTE Directory,
900 LPSTR szPath,
901 LPLONG lpBufferSize)
903 LONG nRequired;
904 int path_len, file_name_len, dir_len;
906 if (Directory >= pHeader->DirectoryCount)
908 *lpBufferSize = 0;
909 return FALSE;
912 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
913 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
914 dir_len = DIR_LENGTH;
916 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
917 if (nRequired < *lpBufferSize)
919 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
920 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
921 szPath[path_len + dir_len] = '\\';
922 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
923 *lpBufferSize = nRequired;
924 return TRUE;
926 *lpBufferSize = nRequired;
927 return FALSE;
930 /***********************************************************************
931 * URLCache_CopyEntry (Internal)
933 * Copies an entry from the cache index file to the Win32 structure
935 * RETURNS
936 * TRUE if the buffer was big enough
937 * FALSE if the buffer was too small
940 static BOOL URLCache_CopyEntry(
941 URLCACHECONTAINER * pContainer,
942 LPCURLCACHE_HEADER pHeader,
943 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
944 LPDWORD lpdwBufferSize,
945 const URL_CACHEFILE_ENTRY * pUrlEntry,
946 BOOL bUnicode)
948 int lenUrl;
949 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
951 if (*lpdwBufferSize >= dwRequiredSize)
953 lpCacheEntryInfo->lpHeaderInfo = NULL;
954 lpCacheEntryInfo->lpszFileExtension = NULL;
955 lpCacheEntryInfo->lpszLocalFileName = NULL;
956 lpCacheEntryInfo->lpszSourceUrlName = NULL;
957 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
958 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
959 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
960 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
961 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
962 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
963 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
964 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
965 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
966 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
967 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
968 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
969 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
970 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
973 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
974 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
975 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
976 if (bUnicode)
977 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
978 else
979 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
980 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
982 /* FIXME: is source url optional? */
983 if (*lpdwBufferSize >= dwRequiredSize)
985 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
986 if (bUnicode)
987 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
988 else
989 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
992 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
993 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
994 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
996 if (pUrlEntry->dwOffsetLocalName)
998 LONG nLocalFilePathSize;
999 LPSTR lpszLocalFileName;
1000 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1001 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1002 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1003 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
1005 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1007 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1009 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1010 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1011 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1013 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1015 if (*lpdwBufferSize >= dwRequiredSize)
1017 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1018 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1019 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1021 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1022 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1023 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1025 if (dwRequiredSize > *lpdwBufferSize)
1027 *lpdwBufferSize = dwRequiredSize;
1028 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1029 return FALSE;
1031 *lpdwBufferSize = dwRequiredSize;
1032 return TRUE;
1036 /***********************************************************************
1037 * URLCache_SetEntryInfo (Internal)
1039 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1040 * according to the flags set by dwFieldControl.
1042 * RETURNS
1043 * TRUE if the buffer was big enough
1044 * FALSE if the buffer was too small
1047 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1049 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1050 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1051 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1052 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1053 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1054 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1055 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1056 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1057 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1058 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1059 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1060 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1061 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1062 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1063 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1064 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1066 return TRUE;
1069 /***********************************************************************
1070 * URLCache_HashKey (Internal)
1072 * Returns the hash key for a given string
1074 * RETURNS
1075 * hash key for the string
1078 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1080 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1081 * but the algorithm and result are not the same!
1083 static const unsigned char lookupTable[256] =
1085 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1086 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1087 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1088 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1089 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1090 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1091 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1092 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1093 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1094 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1095 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1096 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1097 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1098 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1099 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1100 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1101 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1102 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1103 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1104 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1105 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1106 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1107 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1108 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1109 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1110 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1111 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1112 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1113 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1114 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1115 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1116 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1118 BYTE key[4];
1119 DWORD i;
1120 int subscript[sizeof(key) / sizeof(key[0])];
1122 subscript[0] = *lpszKey;
1123 subscript[1] = (char)(*lpszKey + 1);
1124 subscript[2] = (char)(*lpszKey + 2);
1125 subscript[3] = (char)(*lpszKey + 3);
1127 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1128 key[i] = lookupTable[i];
1130 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1132 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1133 key[i] = lookupTable[*lpszKey ^ key[i]];
1136 return *(DWORD *)key;
1139 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1141 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1144 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1146 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1147 return ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) &&
1148 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1151 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1153 /* structure of hash table:
1154 * 448 entries divided into 64 blocks
1155 * each block therefore contains a chain of 7 key/offset pairs
1156 * how position in table is calculated:
1157 * 1. the url is hashed in helper function
1158 * 2. the key % 64 * 8 is the offset
1159 * 3. the key in the hash table is the hash key aligned to 64
1161 * note:
1162 * there can be multiple hash tables in the file and the offset to
1163 * the next one is stored in the header of the hash table
1165 DWORD key = URLCache_HashKey(lpszUrl);
1166 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1167 HASH_CACHEFILE_ENTRY * pHashEntry;
1168 DWORD dwHashTableNumber = 0;
1170 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1172 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1173 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1174 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1176 int i;
1177 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1179 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1180 continue;
1182 /* make sure that it is in fact a hash entry */
1183 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1185 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1186 continue;
1189 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1191 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1192 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1194 /* FIXME: we should make sure that this is the right element
1195 * before returning and claiming that it is. We can do this
1196 * by doing a simple compare between the URL we were given
1197 * and the URL stored in the entry. However, this assumes
1198 * we know the format of all the entries stored in the
1199 * hash table */
1200 *ppHashEntry = pHashElement;
1201 return TRUE;
1205 return FALSE;
1208 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1210 LPSTR urlA;
1211 int url_len;
1212 BOOL ret;
1214 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1215 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1216 if (!urlA)
1218 SetLastError(ERROR_OUTOFMEMORY);
1219 return FALSE;
1221 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, urlA, url_len, NULL, NULL);
1222 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1223 HeapFree(GetProcessHeap(), 0, urlA);
1224 return ret;
1227 /***********************************************************************
1228 * URLCache_HashEntrySetUse (Internal)
1230 * Searches all the hash tables in the index for the given URL and
1231 * sets the use count (stored or'ed with key)
1233 * RETURNS
1234 * TRUE if the entry was found
1235 * FALSE if the entry could not be found
1238 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1240 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1241 return TRUE;
1244 /***********************************************************************
1245 * URLCache_DeleteEntryFromHash (Internal)
1247 * Searches all the hash tables in the index for the given URL and
1248 * then if found deletes the entry.
1250 * RETURNS
1251 * TRUE if the entry was found
1252 * FALSE if the entry could not be found
1255 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1257 pHashEntry->dwHashKey = HASHTABLE_FREE;
1258 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1259 return TRUE;
1262 /***********************************************************************
1263 * URLCache_AddEntryToHash (Internal)
1265 * Searches all the hash tables for a free slot based on the offset
1266 * generated from the hash key. If a free slot is found, the offset and
1267 * key are entered into the hash table.
1269 * RETURNS
1270 * TRUE if the entry was added
1271 * FALSE if the entry could not be added
1274 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1276 /* see URLCache_FindEntryInHash for structure of hash tables */
1278 DWORD key = URLCache_HashKey(lpszUrl);
1279 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1280 HASH_CACHEFILE_ENTRY * pHashEntry;
1281 DWORD dwHashTableNumber = 0;
1283 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1285 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1286 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1287 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1289 int i;
1290 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1292 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1293 break;
1295 /* make sure that it is in fact a hash entry */
1296 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1298 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1299 break;
1302 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1304 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1305 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1307 pHashElement->dwHashKey = key;
1308 pHashElement->dwOffsetEntry = dwOffsetEntry;
1309 return TRUE;
1313 pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1314 if (!pHashEntry)
1315 return FALSE;
1317 pHashEntry->HashTable[offset].dwHashKey = key;
1318 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1319 return TRUE;
1322 /***********************************************************************
1323 * URLCache_CreateHashTable (Internal)
1325 * Creates a new hash table in free space and adds it to the chain of existing
1326 * hash tables.
1328 * RETURNS
1329 * TRUE if the hash table was created
1330 * FALSE if the hash table could not be created
1333 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1335 HASH_CACHEFILE_ENTRY *pHash;
1336 DWORD dwOffset;
1337 int i;
1339 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1341 FIXME("no free space for hash table\n");
1342 SetLastError(ERROR_DISK_FULL);
1343 return NULL;
1346 dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1348 if (pPrevHash)
1349 pPrevHash->dwAddressNext = dwOffset;
1350 else
1351 pHeader->dwOffsetFirstHashTable = dwOffset;
1352 pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1353 pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1354 pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1355 for (i = 0; i < HASHTABLE_SIZE; i++)
1357 pHash->HashTable[i].dwOffsetEntry = 0;
1358 pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1360 return pHash;
1363 /***********************************************************************
1364 * URLCache_EnumHashTables (Internal)
1366 * Enumerates the hash tables in a container.
1368 * RETURNS
1369 * TRUE if an entry was found
1370 * FALSE if there are no more tables to enumerate.
1373 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1375 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1376 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1377 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1379 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1380 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1381 continue;
1382 /* make sure that it is in fact a hash entry */
1383 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1385 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1386 (*pdwHashTableNumber)++;
1387 continue;
1390 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1391 return TRUE;
1393 return FALSE;
1396 /***********************************************************************
1397 * URLCache_EnumHashTableEntries (Internal)
1399 * Enumerates entries in a hash table and returns the next non-free entry.
1401 * RETURNS
1402 * TRUE if an entry was found
1403 * FALSE if the hash table is empty or there are no more entries to
1404 * enumerate.
1407 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1408 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1410 for (; *index < HASHTABLE_SIZE ; (*index)++)
1412 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1413 continue;
1415 *ppHashEntry = &pHashEntry->HashTable[*index];
1416 TRACE("entry found %d\n", *index);
1417 return TRUE;
1419 TRACE("no more entries (%d)\n", *index);
1420 return FALSE;
1423 /***********************************************************************
1424 * GetUrlCacheEntryInfoExA (WININET.@)
1427 BOOL WINAPI GetUrlCacheEntryInfoExA(
1428 LPCSTR lpszUrl,
1429 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1430 LPDWORD lpdwCacheEntryInfoBufSize,
1431 LPSTR lpszReserved,
1432 LPDWORD lpdwReserved,
1433 LPVOID lpReserved,
1434 DWORD dwFlags)
1436 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1437 debugstr_a(lpszUrl),
1438 lpCacheEntryInfo,
1439 lpdwCacheEntryInfoBufSize,
1440 lpszReserved,
1441 lpdwReserved,
1442 lpReserved,
1443 dwFlags);
1445 if ((lpszReserved != NULL) ||
1446 (lpdwReserved != NULL) ||
1447 (lpReserved != NULL))
1449 ERR("Reserved value was not 0\n");
1450 SetLastError(ERROR_INVALID_PARAMETER);
1451 return FALSE;
1453 if (dwFlags != 0)
1454 FIXME("Undocumented flag(s): %x\n", dwFlags);
1455 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1458 /***********************************************************************
1459 * GetUrlCacheEntryInfoA (WININET.@)
1462 BOOL WINAPI GetUrlCacheEntryInfoA(
1463 IN LPCSTR lpszUrlName,
1464 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1465 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1468 LPURLCACHE_HEADER pHeader;
1469 struct _HASH_ENTRY * pHashEntry;
1470 const CACHEFILE_ENTRY * pEntry;
1471 const URL_CACHEFILE_ENTRY * pUrlEntry;
1472 URLCACHECONTAINER * pContainer;
1474 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1476 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1477 return FALSE;
1479 if (!URLCacheContainer_OpenIndex(pContainer))
1480 return FALSE;
1482 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1483 return FALSE;
1485 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1487 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1488 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1489 SetLastError(ERROR_FILE_NOT_FOUND);
1490 return FALSE;
1493 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1494 if (pEntry->dwSignature != URL_SIGNATURE)
1496 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1497 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1498 SetLastError(ERROR_FILE_NOT_FOUND);
1499 return FALSE;
1502 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1503 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1504 if (pUrlEntry->dwOffsetHeaderInfo)
1505 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1507 if (!URLCache_CopyEntry(
1508 pContainer,
1509 pHeader,
1510 lpCacheEntryInfo,
1511 lpdwCacheEntryInfoBufferSize,
1512 pUrlEntry,
1513 FALSE /* ANSI */))
1515 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1516 return FALSE;
1518 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1520 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1522 return TRUE;
1525 /***********************************************************************
1526 * GetUrlCacheEntryInfoW (WININET.@)
1529 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1530 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1531 LPDWORD lpdwCacheEntryInfoBufferSize)
1533 LPURLCACHE_HEADER pHeader;
1534 struct _HASH_ENTRY * pHashEntry;
1535 const CACHEFILE_ENTRY * pEntry;
1536 const URL_CACHEFILE_ENTRY * pUrlEntry;
1537 URLCACHECONTAINER * pContainer;
1539 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1541 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1542 return FALSE;
1544 if (!URLCacheContainer_OpenIndex(pContainer))
1545 return FALSE;
1547 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1548 return FALSE;
1550 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1552 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1553 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1554 SetLastError(ERROR_FILE_NOT_FOUND);
1555 return FALSE;
1558 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1559 if (pEntry->dwSignature != URL_SIGNATURE)
1561 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1562 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1563 SetLastError(ERROR_FILE_NOT_FOUND);
1564 return FALSE;
1567 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1568 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1569 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1571 if (!URLCache_CopyEntry(
1572 pContainer,
1573 pHeader,
1574 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1575 lpdwCacheEntryInfoBufferSize,
1576 pUrlEntry,
1577 TRUE /* UNICODE */))
1579 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1580 return FALSE;
1582 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1584 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1586 return TRUE;
1589 /***********************************************************************
1590 * GetUrlCacheEntryInfoExW (WININET.@)
1593 BOOL WINAPI GetUrlCacheEntryInfoExW(
1594 LPCWSTR lpszUrl,
1595 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1596 LPDWORD lpdwCacheEntryInfoBufSize,
1597 LPWSTR lpszReserved,
1598 LPDWORD lpdwReserved,
1599 LPVOID lpReserved,
1600 DWORD dwFlags)
1602 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1603 debugstr_w(lpszUrl),
1604 lpCacheEntryInfo,
1605 lpdwCacheEntryInfoBufSize,
1606 lpszReserved,
1607 lpdwReserved,
1608 lpReserved,
1609 dwFlags);
1611 if ((lpszReserved != NULL) ||
1612 (lpdwReserved != NULL) ||
1613 (lpReserved != NULL))
1615 ERR("Reserved value was not 0\n");
1616 SetLastError(ERROR_INVALID_PARAMETER);
1617 return FALSE;
1619 if (dwFlags != 0)
1620 FIXME("Undocumented flag(s): %x\n", dwFlags);
1621 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1624 /***********************************************************************
1625 * SetUrlCacheEntryInfoA (WININET.@)
1627 BOOL WINAPI SetUrlCacheEntryInfoA(
1628 LPCSTR lpszUrlName,
1629 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1630 DWORD dwFieldControl)
1632 LPURLCACHE_HEADER pHeader;
1633 struct _HASH_ENTRY * pHashEntry;
1634 CACHEFILE_ENTRY * pEntry;
1635 URLCACHECONTAINER * pContainer;
1637 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1639 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1640 return FALSE;
1642 if (!URLCacheContainer_OpenIndex(pContainer))
1643 return FALSE;
1645 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1646 return FALSE;
1648 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1650 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1651 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1652 SetLastError(ERROR_FILE_NOT_FOUND);
1653 return FALSE;
1656 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1657 if (pEntry->dwSignature != URL_SIGNATURE)
1659 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1660 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1661 SetLastError(ERROR_FILE_NOT_FOUND);
1662 return FALSE;
1665 URLCache_SetEntryInfo(
1666 (URL_CACHEFILE_ENTRY *)pEntry,
1667 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1668 dwFieldControl);
1670 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1672 return TRUE;
1675 /***********************************************************************
1676 * SetUrlCacheEntryInfoW (WININET.@)
1678 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1680 LPURLCACHE_HEADER pHeader;
1681 struct _HASH_ENTRY * pHashEntry;
1682 CACHEFILE_ENTRY * pEntry;
1683 URLCACHECONTAINER * pContainer;
1685 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1687 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1688 return FALSE;
1690 if (!URLCacheContainer_OpenIndex(pContainer))
1691 return FALSE;
1693 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1694 return FALSE;
1696 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1698 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1699 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1700 SetLastError(ERROR_FILE_NOT_FOUND);
1701 return FALSE;
1704 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1705 if (pEntry->dwSignature != URL_SIGNATURE)
1707 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1708 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1709 SetLastError(ERROR_FILE_NOT_FOUND);
1710 return FALSE;
1713 URLCache_SetEntryInfo(
1714 (URL_CACHEFILE_ENTRY *)pEntry,
1715 lpCacheEntryInfo,
1716 dwFieldControl);
1718 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1720 return TRUE;
1723 /***********************************************************************
1724 * RetrieveUrlCacheEntryFileA (WININET.@)
1727 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1728 IN LPCSTR lpszUrlName,
1729 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1730 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1731 IN DWORD dwReserved
1734 LPURLCACHE_HEADER pHeader;
1735 struct _HASH_ENTRY * pHashEntry;
1736 CACHEFILE_ENTRY * pEntry;
1737 URL_CACHEFILE_ENTRY * pUrlEntry;
1738 URLCACHECONTAINER * pContainer;
1740 TRACE("(%s, %p, %p, 0x%08x)\n",
1741 debugstr_a(lpszUrlName),
1742 lpCacheEntryInfo,
1743 lpdwCacheEntryInfoBufferSize,
1744 dwReserved);
1746 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1747 return FALSE;
1749 if (!URLCacheContainer_OpenIndex(pContainer))
1750 return FALSE;
1752 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1753 return FALSE;
1755 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1757 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1758 TRACE("entry %s not found!\n", lpszUrlName);
1759 SetLastError(ERROR_FILE_NOT_FOUND);
1760 return FALSE;
1763 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1764 if (pEntry->dwSignature != URL_SIGNATURE)
1766 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1767 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1768 SetLastError(ERROR_FILE_NOT_FOUND);
1769 return FALSE;
1772 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1773 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1774 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1776 pUrlEntry->dwHitRate++;
1777 pUrlEntry->dwUseCount++;
1778 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1780 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1782 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1783 return FALSE;
1785 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1787 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1789 return TRUE;
1792 /***********************************************************************
1793 * RetrieveUrlCacheEntryFileW (WININET.@)
1796 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1797 IN LPCWSTR lpszUrlName,
1798 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1799 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1800 IN DWORD dwReserved
1803 LPURLCACHE_HEADER pHeader;
1804 struct _HASH_ENTRY * pHashEntry;
1805 CACHEFILE_ENTRY * pEntry;
1806 URL_CACHEFILE_ENTRY * pUrlEntry;
1807 URLCACHECONTAINER * pContainer;
1809 TRACE("(%s, %p, %p, 0x%08x)\n",
1810 debugstr_w(lpszUrlName),
1811 lpCacheEntryInfo,
1812 lpdwCacheEntryInfoBufferSize,
1813 dwReserved);
1815 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1816 return FALSE;
1818 if (!URLCacheContainer_OpenIndex(pContainer))
1819 return FALSE;
1821 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1822 return FALSE;
1824 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1826 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1827 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1828 SetLastError(ERROR_FILE_NOT_FOUND);
1829 return FALSE;
1832 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1833 if (pEntry->dwSignature != URL_SIGNATURE)
1835 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1836 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1837 SetLastError(ERROR_FILE_NOT_FOUND);
1838 return FALSE;
1841 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1842 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1843 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1845 pUrlEntry->dwHitRate++;
1846 pUrlEntry->dwUseCount++;
1847 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1849 if (!URLCache_CopyEntry(
1850 pContainer,
1851 pHeader,
1852 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1853 lpdwCacheEntryInfoBufferSize,
1854 pUrlEntry,
1855 TRUE /* UNICODE */))
1857 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1858 return FALSE;
1860 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1862 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1864 return TRUE;
1867 /***********************************************************************
1868 * UnlockUrlCacheEntryFileA (WININET.@)
1871 BOOL WINAPI UnlockUrlCacheEntryFileA(
1872 IN LPCSTR lpszUrlName,
1873 IN DWORD dwReserved
1876 LPURLCACHE_HEADER pHeader;
1877 struct _HASH_ENTRY * pHashEntry;
1878 CACHEFILE_ENTRY * pEntry;
1879 URL_CACHEFILE_ENTRY * pUrlEntry;
1880 URLCACHECONTAINER * pContainer;
1882 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1884 if (dwReserved)
1886 ERR("dwReserved != 0\n");
1887 SetLastError(ERROR_INVALID_PARAMETER);
1888 return FALSE;
1891 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1892 return FALSE;
1894 if (!URLCacheContainer_OpenIndex(pContainer))
1895 return FALSE;
1897 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1898 return FALSE;
1900 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1902 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1903 TRACE("entry %s not found!\n", lpszUrlName);
1904 SetLastError(ERROR_FILE_NOT_FOUND);
1905 return FALSE;
1908 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1909 if (pEntry->dwSignature != URL_SIGNATURE)
1911 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1912 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1913 SetLastError(ERROR_FILE_NOT_FOUND);
1914 return FALSE;
1917 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1919 if (pUrlEntry->dwUseCount == 0)
1921 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1922 return FALSE;
1924 pUrlEntry->dwUseCount--;
1925 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1927 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1929 return TRUE;
1932 /***********************************************************************
1933 * UnlockUrlCacheEntryFileW (WININET.@)
1936 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1938 LPURLCACHE_HEADER pHeader;
1939 struct _HASH_ENTRY * pHashEntry;
1940 CACHEFILE_ENTRY * pEntry;
1941 URL_CACHEFILE_ENTRY * pUrlEntry;
1942 URLCACHECONTAINER * pContainer;
1944 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1946 if (dwReserved)
1948 ERR("dwReserved != 0\n");
1949 SetLastError(ERROR_INVALID_PARAMETER);
1950 return FALSE;
1953 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1954 return FALSE;
1956 if (!URLCacheContainer_OpenIndex(pContainer))
1957 return FALSE;
1959 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1960 return FALSE;
1962 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1964 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1965 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1966 SetLastError(ERROR_FILE_NOT_FOUND);
1967 return FALSE;
1970 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1971 if (pEntry->dwSignature != URL_SIGNATURE)
1973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1974 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1975 SetLastError(ERROR_FILE_NOT_FOUND);
1976 return FALSE;
1979 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1981 if (pUrlEntry->dwUseCount == 0)
1983 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1984 return FALSE;
1986 pUrlEntry->dwUseCount--;
1987 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1989 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1991 return TRUE;
1994 /***********************************************************************
1995 * CreateUrlCacheEntryA (WININET.@)
1998 BOOL WINAPI CreateUrlCacheEntryA(
1999 IN LPCSTR lpszUrlName,
2000 IN DWORD dwExpectedFileSize,
2001 IN LPCSTR lpszFileExtension,
2002 OUT LPSTR lpszFileName,
2003 IN DWORD dwReserved
2006 DWORD len;
2007 WCHAR *url_name;
2008 WCHAR *file_extension;
2009 WCHAR file_name[MAX_PATH];
2010 BOOL bSuccess = FALSE;
2011 DWORD dwError = 0;
2013 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2014 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2016 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2017 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2018 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2020 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2021 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2023 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2025 bSuccess = TRUE;
2027 else
2029 dwError = GetLastError();
2032 else
2034 dwError = GetLastError();
2036 HeapFree(GetProcessHeap(), 0, file_extension);
2038 else
2040 dwError = GetLastError();
2042 HeapFree(GetProcessHeap(), 0, url_name);
2043 if (!bSuccess)
2044 SetLastError(dwError);
2046 return bSuccess;
2048 /***********************************************************************
2049 * CreateUrlCacheEntryW (WININET.@)
2052 BOOL WINAPI CreateUrlCacheEntryW(
2053 IN LPCWSTR lpszUrlName,
2054 IN DWORD dwExpectedFileSize,
2055 IN LPCWSTR lpszFileExtension,
2056 OUT LPWSTR lpszFileName,
2057 IN DWORD dwReserved
2060 URLCACHECONTAINER * pContainer;
2061 LPURLCACHE_HEADER pHeader;
2062 CHAR szFile[MAX_PATH];
2063 WCHAR szExtension[MAX_PATH];
2064 LPCWSTR lpszUrlPart;
2065 LPCWSTR lpszUrlEnd;
2066 LPCWSTR lpszFileNameExtension;
2067 LPWSTR lpszFileNameNoPath;
2068 int i;
2069 int countnoextension;
2070 BYTE CacheDir;
2071 LONG lBufferSize;
2072 BOOL bFound = FALSE;
2073 int count;
2074 static const WCHAR szWWW[] = {'w','w','w',0};
2076 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2077 debugstr_w(lpszUrlName),
2078 dwExpectedFileSize,
2079 debugstr_w(lpszFileExtension),
2080 lpszFileName,
2081 dwReserved);
2083 if (dwReserved)
2085 ERR("dwReserved != 0\n");
2086 SetLastError(ERROR_INVALID_PARAMETER);
2087 return FALSE;
2090 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2092 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2093 lpszUrlEnd--;
2095 for (lpszUrlPart = lpszUrlEnd;
2096 (lpszUrlPart >= lpszUrlName);
2097 lpszUrlPart--)
2099 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2101 bFound = TRUE;
2102 lpszUrlPart++;
2103 break;
2106 if (!lstrcmpW(lpszUrlPart, szWWW))
2108 lpszUrlPart += lstrlenW(szWWW);
2111 count = lpszUrlEnd - lpszUrlPart;
2113 if (bFound && (count < MAX_PATH))
2115 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2116 if (!len)
2117 return FALSE;
2118 szFile[len] = '\0';
2119 /* FIXME: get rid of illegal characters like \, / and : */
2121 else
2123 FIXME("need to generate a random filename\n");
2126 TRACE("File name: %s\n", debugstr_a(szFile));
2128 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2129 return FALSE;
2131 if (!URLCacheContainer_OpenIndex(pContainer))
2132 return FALSE;
2134 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2135 return FALSE;
2137 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2139 lBufferSize = MAX_PATH * sizeof(WCHAR);
2140 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2142 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2144 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2145 lpszFileNameNoPath >= lpszFileName;
2146 --lpszFileNameNoPath)
2148 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2149 break;
2152 countnoextension = lstrlenW(lpszFileNameNoPath);
2153 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2154 if (lpszFileNameExtension)
2155 countnoextension -= lstrlenW(lpszFileNameExtension);
2156 *szExtension = '\0';
2158 if (lpszFileExtension)
2160 szExtension[0] = '.';
2161 lstrcpyW(szExtension+1, lpszFileExtension);
2164 for (i = 0; i < 255; i++)
2166 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2167 HANDLE hFile;
2168 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2169 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2170 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2171 if (hFile != INVALID_HANDLE_VALUE)
2173 CloseHandle(hFile);
2174 return TRUE;
2178 return FALSE;
2182 /***********************************************************************
2183 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2185 * The bug we are compensating for is that some drongo at Microsoft
2186 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2187 * As a consequence, CommitUrlCacheEntryA has been effectively
2188 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2189 * is still defined as LPCWSTR. The result (other than madness) is
2190 * that we always need to store lpHeaderInfo in CP_ACP rather than
2191 * in UTF16, and we need to avoid converting lpHeaderInfo in
2192 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2193 * result will lose data for arbitrary binary data.
2196 static BOOL WINAPI CommitUrlCacheEntryInternal(
2197 IN LPCWSTR lpszUrlName,
2198 IN LPCWSTR lpszLocalFileName,
2199 IN FILETIME ExpireTime,
2200 IN FILETIME LastModifiedTime,
2201 IN DWORD CacheEntryType,
2202 IN LPBYTE lpHeaderInfo,
2203 IN DWORD dwHeaderSize,
2204 IN LPCWSTR lpszFileExtension,
2205 IN LPCWSTR lpszOriginalUrl
2208 URLCACHECONTAINER * pContainer;
2209 LPURLCACHE_HEADER pHeader;
2210 struct _HASH_ENTRY * pHashEntry;
2211 CACHEFILE_ENTRY * pEntry;
2212 URL_CACHEFILE_ENTRY * pUrlEntry;
2213 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2214 DWORD dwOffsetLocalFileName = 0;
2215 DWORD dwOffsetHeader = 0;
2216 DWORD dwFileSizeLow = 0;
2217 DWORD dwFileSizeHigh = 0;
2218 BYTE cDirectory = 0;
2219 int len;
2220 char achFile[MAX_PATH];
2221 LPSTR lpszUrlNameA = NULL;
2222 char *pchLocalFileName = 0;
2223 DWORD error = ERROR_SUCCESS;
2225 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2226 debugstr_w(lpszUrlName),
2227 debugstr_w(lpszLocalFileName),
2228 CacheEntryType,
2229 lpHeaderInfo,
2230 dwHeaderSize,
2231 debugstr_w(lpszFileExtension),
2232 debugstr_w(lpszOriginalUrl));
2234 if (lpszOriginalUrl)
2235 WARN(": lpszOriginalUrl ignored\n");
2237 if (lpszLocalFileName)
2239 HANDLE hFile;
2241 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2242 if (hFile == INVALID_HANDLE_VALUE)
2244 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2245 return FALSE;
2248 /* Get file size */
2249 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2250 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2252 ERR("couldn't get file size (error is %d)\n", GetLastError());
2253 CloseHandle(hFile);
2254 return FALSE;
2257 CloseHandle(hFile);
2260 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2261 return FALSE;
2263 if (!URLCacheContainer_OpenIndex(pContainer))
2264 return FALSE;
2266 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2267 return FALSE;
2269 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2270 lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2271 if (!lpszUrlNameA)
2273 error = GetLastError();
2274 goto cleanup;
2276 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2278 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2280 FIXME("entry already in cache - don't know what to do!\n");
2282 * SetLastError(ERROR_FILE_NOT_FOUND);
2283 * return FALSE;
2285 goto cleanup;
2288 if (lpszLocalFileName)
2290 BOOL bFound = FALSE;
2292 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2294 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2295 error = ERROR_INVALID_PARAMETER;
2296 goto cleanup;
2299 /* skip container path prefix */
2300 lpszLocalFileName += lstrlenW(pContainer->path);
2302 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2303 pchLocalFileName = achFile;
2305 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2307 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2309 bFound = TRUE;
2310 break;
2314 if (!bFound)
2316 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2317 error = ERROR_INVALID_PARAMETER;
2318 goto cleanup;
2321 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2324 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2325 if (lpszLocalFileName)
2327 len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2328 dwOffsetLocalFileName = dwBytesNeeded;
2329 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2331 if (lpHeaderInfo)
2333 dwOffsetHeader = dwBytesNeeded;
2334 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2337 /* round up to next block */
2338 if (dwBytesNeeded % BLOCKSIZE)
2340 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2341 dwBytesNeeded += BLOCKSIZE;
2344 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2346 ERR("no free entries\n");
2347 error = ERROR_DISK_FULL;
2348 goto cleanup;
2351 /* FindFirstFreeEntry fills in blocks used */
2352 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2353 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2354 pUrlEntry->CacheDir = cDirectory;
2355 pUrlEntry->CacheEntryType = CacheEntryType;
2356 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2357 pUrlEntry->dwExemptDelta = 0;
2358 pUrlEntry->dwHitRate = 0;
2359 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2360 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2361 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2362 pUrlEntry->dwSizeHigh = 0;
2363 pUrlEntry->dwSizeLow = dwFileSizeLow;
2364 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2365 pUrlEntry->dwUseCount = 0;
2366 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2367 pUrlEntry->LastModifiedTime = LastModifiedTime;
2368 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2369 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2370 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2371 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2373 /*** Unknowns ***/
2374 pUrlEntry->dwUnknown1 = 0;
2375 pUrlEntry->dwUnknown2 = 0;
2376 pUrlEntry->dwUnknown3 = 0x60;
2377 pUrlEntry->Unknown4 = 0;
2378 pUrlEntry->wUnknown5 = 0x1010;
2379 pUrlEntry->dwUnknown6 = 0;
2380 pUrlEntry->dwUnknown7 = 0;
2381 pUrlEntry->dwUnknown8 = 0;
2384 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2385 if (dwOffsetLocalFileName)
2386 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2387 if (dwOffsetHeader)
2388 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2390 if (!URLCache_AddEntryToHash(pHeader, lpszUrlNameA, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2392 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2393 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2394 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2395 return FALSE;
2398 cleanup:
2399 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2400 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2402 if (error == ERROR_SUCCESS)
2403 return TRUE;
2404 else
2406 SetLastError(error);
2407 return FALSE;
2411 /***********************************************************************
2412 * CommitUrlCacheEntryA (WININET.@)
2415 BOOL WINAPI CommitUrlCacheEntryA(
2416 IN LPCSTR lpszUrlName,
2417 IN LPCSTR lpszLocalFileName,
2418 IN FILETIME ExpireTime,
2419 IN FILETIME LastModifiedTime,
2420 IN DWORD CacheEntryType,
2421 IN LPBYTE lpHeaderInfo,
2422 IN DWORD dwHeaderSize,
2423 IN LPCSTR lpszFileExtension,
2424 IN LPCSTR lpszOriginalUrl
2427 DWORD len;
2428 WCHAR *url_name;
2429 WCHAR *local_file_name;
2430 WCHAR *original_url = NULL;
2431 BOOL bSuccess = FALSE;
2432 DWORD dwError = 0;
2434 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2435 debugstr_a(lpszUrlName),
2436 debugstr_a(lpszLocalFileName),
2437 CacheEntryType,
2438 lpHeaderInfo,
2439 dwHeaderSize,
2440 debugstr_a(lpszFileExtension),
2441 debugstr_a(lpszOriginalUrl));
2443 if (lpszFileExtension != 0)
2445 SetLastError(ERROR_INVALID_PARAMETER);
2446 return FALSE;
2448 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2449 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2451 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2452 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2453 (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2455 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2456 if (!lpszOriginalUrl ||
2457 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2458 (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2460 if (original_url)
2461 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2462 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2463 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2464 NULL, original_url))
2466 bSuccess = TRUE;
2468 else
2470 dwError = GetLastError();
2472 HeapFree(GetProcessHeap(), 0, original_url);
2474 else
2476 dwError = GetLastError();
2478 HeapFree(GetProcessHeap(), 0, local_file_name);
2480 else
2482 dwError = GetLastError();
2484 HeapFree(GetProcessHeap(), 0, url_name);
2485 if (!bSuccess)
2486 SetLastError(dwError);
2488 return bSuccess;
2491 /***********************************************************************
2492 * CommitUrlCacheEntryW (WININET.@)
2495 BOOL WINAPI CommitUrlCacheEntryW(
2496 IN LPCWSTR lpszUrlName,
2497 IN LPCWSTR lpszLocalFileName,
2498 IN FILETIME ExpireTime,
2499 IN FILETIME LastModifiedTime,
2500 IN DWORD CacheEntryType,
2501 IN LPWSTR lpHeaderInfo,
2502 IN DWORD dwHeaderSize,
2503 IN LPCWSTR lpszFileExtension,
2504 IN LPCWSTR lpszOriginalUrl
2507 DWORD dwError = 0;
2508 BOOL bSuccess = FALSE;
2509 DWORD len = 0;
2510 CHAR *header_info = NULL;
2512 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2513 debugstr_w(lpszUrlName),
2514 debugstr_w(lpszLocalFileName),
2515 CacheEntryType,
2516 lpHeaderInfo,
2517 dwHeaderSize,
2518 debugstr_w(lpszFileExtension),
2519 debugstr_w(lpszOriginalUrl));
2521 if (!lpHeaderInfo ||
2522 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2523 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2525 if (header_info)
2526 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2527 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2528 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2530 bSuccess = TRUE;
2532 else
2534 dwError = GetLastError();
2536 if (header_info)
2538 HeapFree(GetProcessHeap(), 0, header_info);
2539 if (!bSuccess)
2540 SetLastError(dwError);
2543 return bSuccess;
2546 /***********************************************************************
2547 * ReadUrlCacheEntryStream (WININET.@)
2550 BOOL WINAPI ReadUrlCacheEntryStream(
2551 IN HANDLE hUrlCacheStream,
2552 IN DWORD dwLocation,
2553 IN OUT LPVOID lpBuffer,
2554 IN OUT LPDWORD lpdwLen,
2555 IN DWORD dwReserved
2558 /* Get handle to file from 'stream' */
2559 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2561 if (dwReserved != 0)
2563 ERR("dwReserved != 0\n");
2564 SetLastError(ERROR_INVALID_PARAMETER);
2565 return FALSE;
2568 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2570 SetLastError(ERROR_INVALID_HANDLE);
2571 return FALSE;
2574 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2575 return FALSE;
2576 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2579 /***********************************************************************
2580 * RetrieveUrlCacheEntryStreamA (WININET.@)
2583 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2584 IN LPCSTR lpszUrlName,
2585 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2586 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2587 IN BOOL fRandomRead,
2588 IN DWORD dwReserved
2591 /* NOTE: this is not the same as the way that the native
2592 * version allocates 'stream' handles. I did it this way
2593 * as it is much easier and no applications should depend
2594 * on this behaviour. (Native version appears to allocate
2595 * indices into a table)
2597 STREAM_HANDLE * pStream;
2598 HANDLE hFile;
2600 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2601 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2603 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2604 lpCacheEntryInfo,
2605 lpdwCacheEntryInfoBufferSize,
2606 dwReserved))
2608 return NULL;
2611 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2612 GENERIC_READ,
2613 FILE_SHARE_READ,
2614 NULL,
2615 OPEN_EXISTING,
2616 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2617 NULL);
2618 if (hFile == INVALID_HANDLE_VALUE)
2619 return FALSE;
2621 /* allocate handle storage space */
2622 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2623 if (!pStream)
2625 CloseHandle(hFile);
2626 SetLastError(ERROR_OUTOFMEMORY);
2627 return FALSE;
2630 pStream->hFile = hFile;
2631 strcpy(pStream->lpszUrl, lpszUrlName);
2632 return (HANDLE)pStream;
2635 /***********************************************************************
2636 * RetrieveUrlCacheEntryStreamW (WININET.@)
2639 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2640 IN LPCWSTR lpszUrlName,
2641 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2642 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2643 IN BOOL fRandomRead,
2644 IN DWORD dwReserved
2647 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2648 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2649 return NULL;
2652 /***********************************************************************
2653 * UnlockUrlCacheEntryStream (WININET.@)
2656 BOOL WINAPI UnlockUrlCacheEntryStream(
2657 IN HANDLE hUrlCacheStream,
2658 IN DWORD dwReserved
2661 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2663 if (dwReserved != 0)
2665 ERR("dwReserved != 0\n");
2666 SetLastError(ERROR_INVALID_PARAMETER);
2667 return FALSE;
2670 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2672 SetLastError(ERROR_INVALID_HANDLE);
2673 return FALSE;
2676 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2677 return FALSE;
2679 /* close file handle */
2680 CloseHandle(pStream->hFile);
2682 /* free allocated space */
2683 HeapFree(GetProcessHeap(), 0, pStream);
2685 return TRUE;
2689 /***********************************************************************
2690 * DeleteUrlCacheEntryA (WININET.@)
2693 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2695 URLCACHECONTAINER * pContainer;
2696 LPURLCACHE_HEADER pHeader;
2697 struct _HASH_ENTRY * pHashEntry;
2698 CACHEFILE_ENTRY * pEntry;
2700 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2702 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2703 return FALSE;
2705 if (!URLCacheContainer_OpenIndex(pContainer))
2706 return FALSE;
2708 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2709 return FALSE;
2711 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2713 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2714 TRACE("entry %s not found!\n", lpszUrlName);
2715 SetLastError(ERROR_FILE_NOT_FOUND);
2716 return FALSE;
2719 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2720 URLCache_DeleteEntry(pHeader, pEntry);
2722 URLCache_DeleteEntryFromHash(pHashEntry);
2724 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2726 return TRUE;
2729 /***********************************************************************
2730 * DeleteUrlCacheEntryW (WININET.@)
2733 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2735 URLCACHECONTAINER * pContainer;
2736 LPURLCACHE_HEADER pHeader;
2737 struct _HASH_ENTRY * pHashEntry;
2738 CACHEFILE_ENTRY * pEntry;
2739 LPSTR urlA;
2740 int url_len;
2742 TRACE("(%s)\n", debugstr_w(lpszUrlName));
2744 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2745 urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2746 if (!urlA)
2748 SetLastError(ERROR_OUTOFMEMORY);
2749 return FALSE;
2751 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2753 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2755 HeapFree(GetProcessHeap(), 0, urlA);
2756 return FALSE;
2758 if (!URLCacheContainer_OpenIndex(pContainer))
2760 HeapFree(GetProcessHeap(), 0, urlA);
2761 return FALSE;
2763 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2765 HeapFree(GetProcessHeap(), 0, urlA);
2766 return FALSE;
2769 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2771 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2772 TRACE("entry %s not found!\n", debugstr_a(urlA));
2773 HeapFree(GetProcessHeap(), 0, urlA);
2774 SetLastError(ERROR_FILE_NOT_FOUND);
2775 return FALSE;
2778 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2779 URLCache_DeleteEntry(pHeader, pEntry);
2781 URLCache_DeleteEntryFromHash(pHashEntry);
2783 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2785 HeapFree(GetProcessHeap(), 0, urlA);
2786 return TRUE;
2789 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2791 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2792 return TRUE;
2795 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2797 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2798 return TRUE;
2801 /***********************************************************************
2802 * CreateCacheContainerA (WININET.@)
2804 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2805 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2807 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2808 d1, d2, d3, d4, d5, d6, d7, d8);
2809 return TRUE;
2812 /***********************************************************************
2813 * CreateCacheContainerW (WININET.@)
2815 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2816 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2818 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2819 d1, d2, d3, d4, d5, d6, d7, d8);
2820 return TRUE;
2823 /***********************************************************************
2824 * FindFirstUrlCacheContainerA (WININET.@)
2826 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2828 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2829 return NULL;
2832 /***********************************************************************
2833 * FindFirstUrlCacheContainerW (WININET.@)
2835 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2837 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2838 return NULL;
2841 /***********************************************************************
2842 * FindNextUrlCacheContainerA (WININET.@)
2844 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2846 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2847 return FALSE;
2850 /***********************************************************************
2851 * FindNextUrlCacheContainerW (WININET.@)
2853 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2855 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2856 return FALSE;
2859 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2860 LPCSTR lpszUrlSearchPattern,
2861 DWORD dwFlags,
2862 DWORD dwFilter,
2863 GROUPID GroupId,
2864 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2865 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2866 LPVOID lpReserved,
2867 LPDWORD pcbReserved2,
2868 LPVOID lpReserved3
2871 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2872 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2873 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2874 SetLastError(ERROR_FILE_NOT_FOUND);
2875 return NULL;
2878 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2879 LPCWSTR lpszUrlSearchPattern,
2880 DWORD dwFlags,
2881 DWORD dwFilter,
2882 GROUPID GroupId,
2883 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2884 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2885 LPVOID lpReserved,
2886 LPDWORD pcbReserved2,
2887 LPVOID lpReserved3
2890 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2891 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2892 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2893 SetLastError(ERROR_FILE_NOT_FOUND);
2894 return NULL;
2897 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
2899 typedef struct URLCacheFindEntryHandle
2901 DWORD dwMagic;
2902 LPWSTR lpszUrlSearchPattern;
2903 DWORD dwContainerIndex;
2904 DWORD dwHashTableIndex;
2905 DWORD dwHashEntryIndex;
2906 } URLCacheFindEntryHandle;
2908 /***********************************************************************
2909 * FindFirstUrlCacheEntryA (WININET.@)
2912 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2913 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2915 URLCacheFindEntryHandle *pEntryHandle;
2917 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2919 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
2920 if (!pEntryHandle)
2921 return NULL;
2923 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
2924 if (lpszUrlSearchPattern)
2926 int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
2927 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2928 if (!pEntryHandle)
2930 HeapFree(GetProcessHeap(), 0, pEntryHandle);
2931 return NULL;
2933 MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
2935 else
2936 pEntryHandle->lpszUrlSearchPattern = NULL;
2937 pEntryHandle->dwContainerIndex = 0;
2938 pEntryHandle->dwHashTableIndex = 0;
2939 pEntryHandle->dwHashEntryIndex = 0;
2941 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
2943 HeapFree(GetProcessHeap(), 0, pEntryHandle);
2944 return NULL;
2946 return pEntryHandle;
2949 /***********************************************************************
2950 * FindFirstUrlCacheEntryW (WININET.@)
2953 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2954 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2956 URLCacheFindEntryHandle *pEntryHandle;
2958 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2960 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
2961 if (!pEntryHandle)
2962 return NULL;
2964 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
2965 if (lpszUrlSearchPattern)
2967 int len = strlenW(lpszUrlSearchPattern);
2968 pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
2969 if (!pEntryHandle)
2971 HeapFree(GetProcessHeap(), 0, pEntryHandle);
2972 return NULL;
2974 memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
2976 else
2977 pEntryHandle->lpszUrlSearchPattern = NULL;
2978 pEntryHandle->dwContainerIndex = 0;
2979 pEntryHandle->dwHashTableIndex = 0;
2980 pEntryHandle->dwHashEntryIndex = 0;
2982 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
2984 HeapFree(GetProcessHeap(), 0, pEntryHandle);
2985 return NULL;
2987 return pEntryHandle;
2990 /***********************************************************************
2991 * FindNextUrlCacheEntryA (WININET.@)
2993 BOOL WINAPI FindNextUrlCacheEntryA(
2994 HANDLE hEnumHandle,
2995 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2996 LPDWORD lpdwNextCacheEntryInfoBufferSize)
2998 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
2999 URLCACHECONTAINER * pContainer;
3001 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3003 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3005 SetLastError(ERROR_INVALID_HANDLE);
3006 return FALSE;
3009 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3010 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3012 LPURLCACHE_HEADER pHeader;
3013 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3015 if (!URLCacheContainer_OpenIndex(pContainer))
3016 return FALSE;
3018 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3019 return FALSE;
3021 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3022 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3024 const struct _HASH_ENTRY *pHashEntry = NULL;
3025 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3026 pEntryHandle->dwHashEntryIndex++)
3028 const URL_CACHEFILE_ENTRY *pUrlEntry;
3029 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3031 if (pEntry->dwSignature != URL_SIGNATURE)
3032 continue;
3034 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3035 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3036 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3038 if (!URLCache_CopyEntry(
3039 pContainer,
3040 pHeader,
3041 lpNextCacheEntryInfo,
3042 lpdwNextCacheEntryInfoBufferSize,
3043 pUrlEntry,
3044 FALSE /* not UNICODE */))
3046 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3047 return FALSE;
3049 TRACE("Local File Name: %s\n", debugstr_a(lpNextCacheEntryInfo->lpszLocalFileName));
3051 /* increment the current index so that next time the function
3052 * is called the next entry is returned */
3053 pEntryHandle->dwHashEntryIndex++;
3054 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3055 return TRUE;
3059 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3062 SetLastError(ERROR_NO_MORE_ITEMS);
3063 return FALSE;
3066 BOOL WINAPI FindNextUrlCacheEntryW(
3067 HANDLE hEnumHandle,
3068 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3069 LPDWORD lpdwNextCacheEntryInfoBufferSize
3072 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3073 return FALSE;
3076 /***********************************************************************
3077 * FindCloseUrlCache (WININET.@)
3079 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3081 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3083 TRACE("(%p)\n", hEnumHandle);
3085 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3087 SetLastError(ERROR_INVALID_HANDLE);
3088 return FALSE;
3091 pEntryHandle->dwMagic = 0;
3092 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3093 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3095 return TRUE;
3098 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3099 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3101 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3102 dwSearchCondition, lpGroupId, lpReserved);
3103 return NULL;
3106 BOOL WINAPI FindNextUrlCacheEntryExA(
3107 HANDLE hEnumHandle,
3108 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3109 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3110 LPVOID lpReserved,
3111 LPDWORD pcbReserved2,
3112 LPVOID lpReserved3
3115 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3116 lpReserved, pcbReserved2, lpReserved3);
3117 return FALSE;
3120 BOOL WINAPI FindNextUrlCacheEntryExW(
3121 HANDLE hEnumHandle,
3122 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3123 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3124 LPVOID lpReserved,
3125 LPDWORD pcbReserved2,
3126 LPVOID lpReserved3
3129 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3130 lpReserved, pcbReserved2, lpReserved3);
3131 return FALSE;
3134 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3136 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3137 return FALSE;
3140 /***********************************************************************
3141 * CreateUrlCacheGroup (WININET.@)
3144 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3146 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3147 return FALSE;
3150 /***********************************************************************
3151 * DeleteUrlCacheGroup (WININET.@)
3154 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3156 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3157 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3158 return FALSE;
3161 /***********************************************************************
3162 * SetUrlCacheEntryGroupA (WININET.@)
3165 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3166 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3167 LPVOID lpReserved)
3169 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3170 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3171 pbGroupAttributes, cbGroupAttributes, lpReserved);
3172 SetLastError(ERROR_FILE_NOT_FOUND);
3173 return FALSE;
3176 /***********************************************************************
3177 * SetUrlCacheEntryGroupW (WININET.@)
3180 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3181 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3182 LPVOID lpReserved)
3184 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3185 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3186 pbGroupAttributes, cbGroupAttributes, lpReserved);
3187 SetLastError(ERROR_FILE_NOT_FOUND);
3188 return FALSE;
3191 /***********************************************************************
3192 * GetUrlCacheConfigInfoW (WININET.@)
3194 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3196 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3197 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3198 return FALSE;
3201 /***********************************************************************
3202 * GetUrlCacheConfigInfoA (WININET.@)
3204 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3206 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3208 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3209 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3210 return FALSE;
3213 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3214 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3215 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3217 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3218 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3219 lpdwGroupInfo, lpReserved);
3220 return FALSE;
3223 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3224 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3225 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3227 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3228 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3229 lpdwGroupInfo, lpReserved);
3230 return FALSE;
3233 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3234 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3236 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3237 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3238 return TRUE;
3241 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3242 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3244 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3245 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3246 return TRUE;
3249 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3251 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3252 return TRUE;
3255 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3257 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3258 return TRUE;
3261 /***********************************************************************
3262 * DeleteIE3Cache (WININET.@)
3264 * Deletes the files used by the IE3 URL caching system.
3266 * PARAMS
3267 * hWnd [I] A dummy window.
3268 * hInst [I] Instance of process calling the function.
3269 * lpszCmdLine [I] Options used by function.
3270 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3272 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3274 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3275 return 0;
3278 /***********************************************************************
3279 * IsUrlCacheEntryExpiredA (WININET.@)
3281 * PARAMS
3282 * url [I] Url
3283 * dwFlags [I] Unknown
3284 * pftLastModified [O] Last modified time
3286 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3288 LPURLCACHE_HEADER pHeader;
3289 struct _HASH_ENTRY * pHashEntry;
3290 const CACHEFILE_ENTRY * pEntry;
3291 const URL_CACHEFILE_ENTRY * pUrlEntry;
3292 URLCACHECONTAINER * pContainer;
3294 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3296 if (!URLCacheContainers_FindContainerA(url, &pContainer))
3297 return FALSE;
3299 if (!URLCacheContainer_OpenIndex(pContainer))
3300 return FALSE;
3302 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3303 return FALSE;
3305 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3307 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3308 TRACE("entry %s not found!\n", url);
3309 SetLastError(ERROR_FILE_NOT_FOUND);
3310 return FALSE;
3313 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3314 if (pEntry->dwSignature != URL_SIGNATURE)
3316 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3317 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3318 SetLastError(ERROR_FILE_NOT_FOUND);
3319 return FALSE;
3322 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3324 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3326 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3328 return TRUE;
3331 /***********************************************************************
3332 * IsUrlCacheEntryExpiredW (WININET.@)
3334 * PARAMS
3335 * url [I] Url
3336 * dwFlags [I] Unknown
3337 * pftLastModified [O] Last modified time
3339 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3341 LPURLCACHE_HEADER pHeader;
3342 struct _HASH_ENTRY * pHashEntry;
3343 const CACHEFILE_ENTRY * pEntry;
3344 const URL_CACHEFILE_ENTRY * pUrlEntry;
3345 URLCACHECONTAINER * pContainer;
3347 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3349 if (!URLCacheContainers_FindContainerW(url, &pContainer))
3350 return FALSE;
3352 if (!URLCacheContainer_OpenIndex(pContainer))
3353 return FALSE;
3355 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3356 return FALSE;
3358 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3360 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3361 TRACE("entry %s not found!\n", debugstr_w(url));
3362 SetLastError(ERROR_FILE_NOT_FOUND);
3363 return FALSE;
3366 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3367 if (pEntry->dwSignature != URL_SIGNATURE)
3369 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3370 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3371 SetLastError(ERROR_FILE_NOT_FOUND);
3372 return FALSE;
3375 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3377 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3379 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3381 return TRUE;