2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 Robert Shearman
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #define COM_NO_WINDOWS_H
45 #include "wine/unicode.h"
46 #include "wine/list.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
51 #define ENTRY_START_OFFSET 0x4000
54 #define HASHTABLE_SIZE 448
55 #define HASHTABLE_BLOCKSIZE 7
56 #define HASHTABLE_FREE 3
57 #define ALLOCATION_TABLE_OFFSET 0x250
58 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
59 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
61 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
62 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
63 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
64 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
65 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
67 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
69 typedef struct _CACHEFILE_ENTRY
73 DWORD dwSignature; /* e.g. "URL " */
74 /* CHAR szSignature[4];
76 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
79 typedef struct _URL_CACHEFILE_ENTRY
81 CACHEFILE_ENTRY CacheFileEntry;
82 FILETIME LastModifiedTime;
83 FILETIME LastAccessTime;
84 WORD wExpiredDate; /* expire date in dos format */
85 WORD wExpiredTime; /* expire time in dos format */
86 DWORD dwUnknown1; /* usually zero */
87 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
88 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
89 DWORD dwUnknown2; /* usually zero */
90 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
91 DWORD dwUnknown3; /* usually 0x60 */
92 DWORD dwOffsetUrl; /* usually 0x68 */
93 BYTE CacheDir; /* index of cache directory this url is stored in */
94 BYTE Unknown4; /* usually zero */
95 WORD wUnknown5; /* usually 0x1010 */
96 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
97 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
98 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
99 DWORD dwHeaderInfoSize;
100 DWORD dwUnknown6; /* usually zero */
101 WORD wLastSyncDate; /* last sync date in dos format */
102 WORD wLastSyncTime; /* last sync time in dos format */
103 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
104 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
105 WORD wUnknownDate; /* usually same as wLastSyncDate */
106 WORD wUnknownTime; /* usually same as wLastSyncTime */
107 DWORD dwUnknown7; /* usually zero */
108 DWORD dwUnknown8; /* usually zero */
109 CHAR szSourceUrlName[1]; /* start of url */
110 /* packing to dword align start of next field */
111 /* CHAR szLocalFileName[]; (local file name exluding path) */
112 /* packing to dword align start of next field */
113 /* CHAR szHeaderInfo[]; (header info) */
114 } URL_CACHEFILE_ENTRY;
122 typedef struct _HASH_CACHEFILE_ENTRY
124 CACHEFILE_ENTRY CacheFileEntry;
126 DWORD dwHashTableNumber;
127 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
128 } HASH_CACHEFILE_ENTRY;
130 typedef struct _DIRECTORY_DATA
133 char filename[DIR_LENGTH];
136 typedef struct _URLCACHE_HEADER
138 char szSignature[28];
140 DWORD dwOffsetFirstHashTable;
141 DWORD dwIndexCapacityInBlocks;
142 DWORD dwBlocksInUse; /* is this right? */
144 DWORD dwCacheLimitLow; /* disk space limit for cache */
145 DWORD dwCacheLimitHigh; /* disk space limit for cache */
146 DWORD dwUnknown4; /* current disk space usage for cache? */
147 DWORD dwUnknown5; /* current disk space usage for cache? */
148 DWORD dwUnknown6; /* possibly a flag? */
150 BYTE DirectoryCount; /* number of directory_data's */
151 BYTE Unknown8[3]; /* just padding? */
152 DIRECTORY_DATA directory_data[1]; /* first directory entry */
153 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
154 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
156 typedef struct _STREAM_HANDLE
162 typedef struct _URLCACHECONTAINER
164 struct list entry; /* part of a list */
165 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
166 LPWSTR path; /* path to url container directory */
167 HANDLE hMapping; /* handle of file mapping */
168 DWORD file_size; /* size of file when mapping was opened */
169 HANDLE hMutex; /* hande of mutex */
173 /* List of all containers available */
174 static struct list UrlContainers = LIST_INIT(UrlContainers);
177 /***********************************************************************
178 * URLCache_PathToObjectName (Internal)
180 * Converts a path to a name suitable for use as a Win32 object name.
181 * Replaces '\\' characters in-place with the specified character
182 * (usually '_' or '!')
188 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
190 for (; *lpszPath; lpszPath++)
192 if (*lpszPath == '\\')
197 /***********************************************************************
198 * URLCacheContainer_OpenIndex (Internal)
200 * Opens the index file and saves mapping handle in hCacheIndexMapping
207 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
210 WCHAR wszFilePath[MAX_PATH];
213 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
214 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
216 if (pContainer->hMapping)
219 strcpyW(wszFilePath, pContainer->path);
220 strcatW(wszFilePath, wszIndex);
222 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
223 if (hFile == INVALID_HANDLE_VALUE)
225 FIXME("need to create cache index file\n");
229 dwFileSize = GetFileSize(hFile, NULL);
230 if (dwFileSize == INVALID_FILE_SIZE)
235 FIXME("need to create cache index file\n");
239 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
240 URLCache_PathToObjectName(wszFilePath, '_');
241 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
242 if (!pContainer->hMapping)
243 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
245 if (!pContainer->hMapping)
247 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
254 /***********************************************************************
255 * URLCacheContainer_CloseIndex (Internal)
263 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
265 CloseHandle(pContainer->hMapping);
266 pContainer->hMapping = NULL;
269 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
271 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
272 int path_len = strlenW(path);
273 int cache_prefix_len = strlenW(cache_prefix);
280 pContainer->hMapping = NULL;
281 pContainer->file_size = 0;
283 pContainer->path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
284 if (!pContainer->path)
286 HeapFree(GetProcessHeap(), 0, pContainer);
290 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
292 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
293 if (!pContainer->cache_prefix)
295 HeapFree(GetProcessHeap(), 0, pContainer->path);
296 HeapFree(GetProcessHeap(), 0, pContainer);
300 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
302 CharLowerW(mutex_name);
303 URLCache_PathToObjectName(mutex_name, '!');
305 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
307 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
308 HeapFree(GetProcessHeap(), 0, pContainer->path);
309 HeapFree(GetProcessHeap(), 0, pContainer);
313 list_add_head(&UrlContainers, &pContainer->entry);
318 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
320 list_remove(&pContainer->entry);
322 URLCacheContainer_CloseIndex(pContainer);
323 CloseHandle(pContainer->hMutex);
324 HeapFree(GetProcessHeap(), 0, pContainer->path);
325 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
326 HeapFree(GetProcessHeap(), 0, pContainer);
329 void URLCacheContainers_CreateDefaults()
331 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
332 static const WCHAR UrlPrefix[] = {0};
333 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
334 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
335 static const WCHAR CookieSuffix[] = {0};
336 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
339 int nFolder; /* CSIDL_* constant */
340 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
341 const WCHAR * cache_prefix; /* prefix used to reference the container */
342 } DefaultContainerData[] =
344 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
345 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
346 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
350 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
352 WCHAR wszCachePath[MAX_PATH];
353 WCHAR wszMutexName[MAX_PATH];
354 int path_len, suffix_len;
356 if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
358 ERR("Couldn't get path for default container %lu\n", i);
361 path_len = strlenW(wszCachePath);
362 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
364 if (path_len + suffix_len + 2 > MAX_PATH)
366 ERR("Path too long\n");
370 wszCachePath[path_len] = '\\';
372 strcpyW(wszMutexName, wszCachePath);
376 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
377 wszCachePath[path_len + suffix_len + 1] = '\\';
378 wszCachePath[path_len + suffix_len + 2] = '\0';
381 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
385 void URLCacheContainers_DeleteAll()
387 while(!list_empty(&UrlContainers))
388 URLCacheContainer_DeleteContainer(
389 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
393 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
395 struct list * cursor;
397 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
399 LIST_FOR_EACH(cursor, &UrlContainers)
401 URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
402 int prefix_len = strlenW(pContainer->cache_prefix);
403 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
405 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
406 *ppContainer = pContainer;
410 ERR("no container found\n");
411 SetLastError(ERROR_FILE_NOT_FOUND);
415 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
419 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
420 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
422 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
423 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
424 HeapFree(GetProcessHeap(), 0, lpwszUrl);
430 /***********************************************************************
431 * URLCacheContainer_LockIndex (Internal)
434 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
438 URLCACHE_HEADER * pHeader;
441 WaitForSingleObject(pContainer->hMutex, INFINITE);
443 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
447 ReleaseMutex(pContainer->hMutex);
448 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
451 pHeader = (URLCACHE_HEADER *)pIndexData;
453 /* file has grown - we need to remap to prevent us getting
454 * access violations when we try and access beyond the end
455 * of the memory mapped file */
456 if (pHeader->dwFileSize != pContainer->file_size)
458 URLCacheContainer_CloseIndex(pContainer);
459 if (!URLCacheContainer_OpenIndex(pContainer))
461 ReleaseMutex(pContainer->hMutex);
464 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
468 ReleaseMutex(pContainer->hMutex);
469 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
472 pHeader = (URLCACHE_HEADER *)pIndexData;
475 TRACE("Signature: %s, file size: %ld bytes\n", pHeader->szSignature, pHeader->dwFileSize);
477 for (index = 0; index < pHeader->DirectoryCount; index++)
479 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
485 /***********************************************************************
486 * URLCacheContainer_UnlockIndex (Internal)
489 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
492 ReleaseMutex(pContainer->hMutex);
493 return UnmapViewOfFile(pHeader);
498 #define CHAR_BIT (8 * sizeof(CHAR))
501 /***********************************************************************
502 * URLCache_Allocation_BlockIsFree (Internal)
504 * Is the specified block number free?
511 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
513 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
514 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
517 /***********************************************************************
518 * URLCache_Allocation_BlockFree (Internal)
520 * Marks the specified block as free
526 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
528 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
529 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
532 /***********************************************************************
533 * URLCache_Allocation_BlockAllocate (Internal)
535 * Marks the specified block as allocated
541 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
543 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
544 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
547 /***********************************************************************
548 * URLCache_FindFirstFreeEntry (Internal)
550 * Finds and allocates the first block of free space big enough and
551 * sets ppEntry to point to it.
554 * TRUE if it had enough space
555 * FALSE if it couldn't find enough space
558 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
560 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
563 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
565 for (dwFreeCounter = 0;
566 dwFreeCounter < dwBlocksNeeded &&
567 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
568 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
570 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
572 if (dwFreeCounter == dwBlocksNeeded)
575 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
576 for (index = 0; index < dwBlocksNeeded; index++)
577 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
578 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
579 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
583 FIXME("Grow file\n");
587 /***********************************************************************
588 * URLCache_DeleteEntry (Internal)
590 * Deletes the specified entry and frees the space allocated to it
593 * TRUE if it succeeded
597 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
599 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
603 /***********************************************************************
604 * URLCache_LocalFileNameToPathW (Internal)
606 * Copies the full path to the specified buffer given the local file
607 * name and the index of the directory it is in. Always sets value in
608 * lpBufferSize to the required buffer size (in bytes).
611 * TRUE if the buffer was big enough
612 * FALSE if the buffer was too small
615 static BOOL URLCache_LocalFileNameToPathW(
616 const URLCACHECONTAINER * pContainer,
617 LPCURLCACHE_HEADER pHeader,
618 LPCSTR szLocalFileName,
624 int path_len = strlenW(pContainer->path);
625 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
626 if (Directory >= pHeader->DirectoryCount)
632 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
633 if (nRequired < *lpBufferSize)
637 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
638 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
639 wszPath[dir_len + path_len] = '\\';
640 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
641 *lpBufferSize = nRequired;
644 *lpBufferSize = nRequired;
648 /***********************************************************************
649 * URLCache_LocalFileNameToPathA (Internal)
651 * Copies the full path to the specified buffer given the local file
652 * name and the index of the directory it is in. Always sets value in
653 * lpBufferSize to the required buffer size.
656 * TRUE if the buffer was big enough
657 * FALSE if the buffer was too small
660 static BOOL URLCache_LocalFileNameToPathA(
661 const URLCACHECONTAINER * pContainer,
662 LPCURLCACHE_HEADER pHeader,
663 LPCSTR szLocalFileName,
669 int path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
670 int file_name_len = strlen(szLocalFileName);
671 int dir_len = DIR_LENGTH;
672 if (Directory >= pHeader->DirectoryCount)
678 nRequired = (path_len + dir_len + file_name_len + 1) * sizeof(WCHAR);
679 if (nRequired < *lpBufferSize)
681 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
682 strncpy(szPath, pHeader->directory_data[Directory].filename, DIR_LENGTH);
683 szPath[dir_len + path_len] = '\\';
684 strcpy(szPath + dir_len + path_len + 1, szLocalFileName);
685 *lpBufferSize = nRequired;
688 *lpBufferSize = nRequired;
692 /***********************************************************************
693 * URLCache_CopyEntry (Internal)
695 * Copies an entry from the cache index file to the Win32 structure
698 * TRUE if the buffer was big enough
699 * FALSE if the buffer was too small
702 static BOOL URLCache_CopyEntry(
703 URLCACHECONTAINER * pContainer,
704 LPCURLCACHE_HEADER pHeader,
705 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
706 LPDWORD lpdwBufferSize,
707 URL_CACHEFILE_ENTRY * pUrlEntry,
711 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
713 if (*lpdwBufferSize >= dwRequiredSize)
715 lpCacheEntryInfo->lpHeaderInfo = NULL;
716 lpCacheEntryInfo->lpszFileExtension = NULL;
717 lpCacheEntryInfo->lpszLocalFileName = NULL;
718 lpCacheEntryInfo->lpszSourceUrlName = NULL;
719 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
720 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
721 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
722 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
723 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
724 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
725 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
726 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
727 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
728 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
729 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
730 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
731 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
732 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
735 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
736 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
737 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
739 lenUrl = MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, NULL, 0);
741 lenUrl = strlen(pUrlEntry->szSourceUrlName);
742 dwRequiredSize += lenUrl + 1;
744 /* FIXME: is source url optional? */
745 if (*lpdwBufferSize >= dwRequiredSize)
747 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
749 MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
751 memcpy(lpCacheEntryInfo->lpszSourceUrlName, pUrlEntry->szSourceUrlName, (lenUrl + 1) * sizeof(CHAR));
754 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
755 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
756 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
758 if (pUrlEntry->dwOffsetLocalName)
760 LONG nLocalFilePathSize;
761 LPSTR lpszLocalFileName;
762 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
763 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
764 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
765 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
767 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
769 dwRequiredSize += nLocalFilePathSize;
771 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
772 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
773 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
775 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
777 if (*lpdwBufferSize >= dwRequiredSize)
779 lpCacheEntryInfo->lpHeaderInfo = (LPSTR)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
780 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
781 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
783 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
784 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
785 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
787 if (dwRequiredSize > *lpdwBufferSize)
789 *lpdwBufferSize = dwRequiredSize;
790 SetLastError(ERROR_INSUFFICIENT_BUFFER);
793 *lpdwBufferSize = dwRequiredSize;
798 /***********************************************************************
799 * URLCache_SetEntryInfo (Internal)
801 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
802 * according the the flags set by dwFieldControl.
805 * TRUE if the buffer was big enough
806 * FALSE if the buffer was too small
809 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
811 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
812 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
813 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
814 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
815 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
816 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
817 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
818 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
819 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
820 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
821 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
822 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
823 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
824 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
825 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
826 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
831 /***********************************************************************
832 * URLCache_HashKey (Internal)
834 * Returns the hash key for a given string
837 * hash key for the string
840 static DWORD URLCache_HashKey(LPCSTR lpszKey)
842 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
843 * but the algorithm and result are not the same!
845 static const unsigned char lookupTable[256] =
847 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
848 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
849 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
850 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
851 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
852 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
853 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
854 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
855 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
856 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
857 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
858 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
859 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
860 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
861 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
862 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
863 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
864 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
865 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
866 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
867 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
868 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
869 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
870 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
871 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
872 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
873 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
874 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
875 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
876 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
877 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
878 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
882 int subscript[sizeof(key) / sizeof(key[0])];
884 subscript[0] = *lpszKey;
885 subscript[1] = (char)(*lpszKey + 1);
886 subscript[2] = (char)(*lpszKey + 2);
887 subscript[3] = (char)(*lpszKey + 3);
889 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
890 key[i] = lookupTable[i];
892 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
894 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
895 key[i] = lookupTable[*lpszKey ^ key[i]];
898 return *(DWORD *)key;
901 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
903 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
906 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
908 /* structure of hash table:
909 * 448 entries divided into 64 blocks
910 * each block therefore contains a chain of 7 key/offset pairs
911 * how position in table is calculated:
912 * 1. the url is hashed in helper function
913 * 2. the key % 64 * 8 is the offset
914 * 3. the key in the hash table is the hash key aligned to 64
917 * there can be multiple hash tables in the file and the offset to
918 * the next one is stored in the header of the hash table
920 DWORD key = URLCache_HashKey(lpszUrl);
921 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
922 HASH_CACHEFILE_ENTRY * pHashEntry;
923 DWORD dwHashTableNumber = 0;
925 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
927 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
928 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
929 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
932 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
934 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
937 /* make sure that it is in fact a hash entry */
938 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
940 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
944 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
946 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
947 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
949 /* FIXME: we should make sure that this is the right element
950 * before returning and claiming that it is. We can do this
951 * by doing a simple compare between the URL we were given
952 * and the URL stored in the entry. However, this assumes
953 * we know the format of all the entries stored in the
955 *ppHashEntry = pHashElement;
963 /***********************************************************************
964 * URLCache_FindEntryInHash (Internal)
966 * Searches all the hash tables in the index for the given URL and
967 * returns the entry, if it was found, in ppEntry
970 * TRUE if the entry was found
971 * FALSE if the entry could not be found
974 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
976 struct _HASH_ENTRY * pHashEntry;
977 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
979 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
985 /***********************************************************************
986 * URLCache_HashEntrySetUse (Internal)
988 * Searches all the hash tables in the index for the given URL and
989 * sets the use count (stored or'ed with key)
992 * TRUE if the entry was found
993 * FALSE if the entry could not be found
996 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
998 struct _HASH_ENTRY * pHashEntry;
999 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1001 pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1007 /***********************************************************************
1008 * URLCache_DeleteEntryFromHash (Internal)
1010 * Searches all the hash tables in the index for the given URL and
1011 * then if found deletes the entry.
1014 * TRUE if the entry was found
1015 * FALSE if the entry could not be found
1018 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1020 struct _HASH_ENTRY * pHashEntry;
1021 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1023 pHashEntry->dwHashKey = HASHTABLE_FREE;
1024 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1030 /***********************************************************************
1031 * URLCache_AddEntryToHash (Internal)
1033 * Searches all the hash tables for a free slot based on the offset
1034 * generated from the hash key. If a free slot is found, the offset and
1035 * key are entered into the hash table.
1038 * TRUE if the entry was added
1039 * FALSE if the entry could not be added
1042 static BOOL URLCache_AddEntryToHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1044 /* see URLCache_FindEntryInHash for structure of hash tables */
1046 DWORD key = URLCache_HashKey(lpszUrl);
1047 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1048 HASH_CACHEFILE_ENTRY * pHashEntry;
1049 DWORD dwHashTableNumber = 0;
1051 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1053 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1054 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1055 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1058 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1060 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1063 /* make sure that it is in fact a hash entry */
1064 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1066 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1070 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1072 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1073 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1075 pHashElement->dwHashKey = key;
1076 pHashElement->dwOffsetEntry = dwOffsetEntry;
1081 FIXME("need to create another hash table\n");
1085 /***********************************************************************
1086 * GetUrlCacheEntryInfoExA (WININET.@)
1089 BOOL WINAPI GetUrlCacheEntryInfoExA(
1091 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1092 LPDWORD lpdwCacheEntryInfoBufSize,
1094 LPDWORD lpdwReserved,
1098 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1099 debugstr_a(lpszUrl),
1101 lpdwCacheEntryInfoBufSize,
1107 if ((lpszReserved != NULL) ||
1108 (lpdwReserved != NULL) ||
1109 (lpReserved != NULL))
1111 ERR("Reserved value was not 0\n");
1112 SetLastError(ERROR_INVALID_PARAMETER);
1116 FIXME("Undocumented flag(s): %lx\n", dwFlags);
1117 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1120 /***********************************************************************
1121 * GetUrlCacheEntryInfoA (WININET.@)
1124 BOOL WINAPI GetUrlCacheEntryInfoA(
1125 IN LPCSTR lpszUrlName,
1126 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1127 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1130 LPURLCACHE_HEADER pHeader;
1131 CACHEFILE_ENTRY * pEntry;
1132 URL_CACHEFILE_ENTRY * pUrlEntry;
1133 URLCACHECONTAINER * pContainer;
1135 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1137 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1140 if (!URLCacheContainer_OpenIndex(pContainer))
1143 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1146 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1148 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1149 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1150 SetLastError(ERROR_FILE_NOT_FOUND);
1154 if (pEntry->dwSignature != URL_SIGNATURE)
1156 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1157 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1158 SetLastError(ERROR_FILE_NOT_FOUND);
1162 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1163 TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1164 if (pUrlEntry->dwOffsetHeaderInfo)
1165 TRACE("Header info: %s\n", debugstr_a((LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1167 if (!URLCache_CopyEntry(
1171 lpdwCacheEntryInfoBufferSize,
1175 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1178 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1180 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1185 /***********************************************************************
1186 * GetUrlCacheEntryInfoW (WININET.@)
1189 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1190 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1191 LPDWORD lpdwCacheEntryInfoBufferSize)
1193 LPURLCACHE_HEADER pHeader;
1194 CACHEFILE_ENTRY * pEntry;
1195 URL_CACHEFILE_ENTRY * pUrlEntry;
1196 URLCACHECONTAINER * pContainer;
1200 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1202 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1203 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1206 SetLastError(ERROR_OUTOFMEMORY);
1209 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1211 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1213 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1217 if (!URLCacheContainer_OpenIndex(pContainer))
1219 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1223 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1225 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1229 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1231 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1232 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1233 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1234 SetLastError(ERROR_FILE_NOT_FOUND);
1237 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1239 if (pEntry->dwSignature != URL_SIGNATURE)
1241 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1242 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1243 SetLastError(ERROR_FILE_NOT_FOUND);
1247 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1248 TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1249 TRACE("Header info: %s\n", debugstr_a((LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1251 if (!URLCache_CopyEntry(
1254 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1255 lpdwCacheEntryInfoBufferSize,
1257 TRUE /* UNICODE */))
1259 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1262 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1264 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1269 /***********************************************************************
1270 * GetUrlCacheEntryInfoExW (WININET.@)
1273 BOOL WINAPI GetUrlCacheEntryInfoExW(
1275 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1276 LPDWORD lpdwCacheEntryInfoBufSize,
1277 LPWSTR lpszReserved,
1278 LPDWORD lpdwReserved,
1282 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1283 debugstr_w(lpszUrl),
1285 lpdwCacheEntryInfoBufSize,
1291 if ((lpszReserved != NULL) ||
1292 (lpdwReserved != NULL) ||
1293 (lpReserved != NULL))
1295 ERR("Reserved value was not 0\n");
1296 SetLastError(ERROR_INVALID_PARAMETER);
1300 FIXME("Undocumented flag(s): %lx\n", dwFlags);
1301 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1304 /***********************************************************************
1305 * SetUrlCacheEntryInfoA (WININET.@)
1307 BOOL WINAPI SetUrlCacheEntryInfoA(
1309 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1310 DWORD dwFieldControl)
1312 LPURLCACHE_HEADER pHeader;
1313 CACHEFILE_ENTRY * pEntry;
1314 URLCACHECONTAINER * pContainer;
1316 TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1318 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1321 if (!URLCacheContainer_OpenIndex(pContainer))
1324 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1327 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1329 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1330 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1331 SetLastError(ERROR_FILE_NOT_FOUND);
1335 if (pEntry->dwSignature != URL_SIGNATURE)
1337 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1338 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1339 SetLastError(ERROR_FILE_NOT_FOUND);
1343 URLCache_SetEntryInfo(
1344 (URL_CACHEFILE_ENTRY *)pEntry,
1345 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1348 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1353 /***********************************************************************
1354 * SetUrlCacheEntryInfoW (WININET.@)
1356 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1358 LPURLCACHE_HEADER pHeader;
1359 CACHEFILE_ENTRY * pEntry;
1360 URLCACHECONTAINER * pContainer;
1364 TRACE("(%s, %p, 0x%08lx)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1366 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1367 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1370 SetLastError(ERROR_OUTOFMEMORY);
1373 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1375 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1377 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1381 if (!URLCacheContainer_OpenIndex(pContainer))
1383 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1387 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1389 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1393 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1395 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1396 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1397 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1398 SetLastError(ERROR_FILE_NOT_FOUND);
1401 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1403 if (pEntry->dwSignature != URL_SIGNATURE)
1405 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1406 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1407 SetLastError(ERROR_FILE_NOT_FOUND);
1411 URLCache_SetEntryInfo(
1412 (URL_CACHEFILE_ENTRY *)pEntry,
1416 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1421 /***********************************************************************
1422 * RetrieveUrlCacheEntryFileA (WININET.@)
1425 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1426 IN LPCSTR lpszUrlName,
1427 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1428 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1432 LPURLCACHE_HEADER pHeader;
1433 CACHEFILE_ENTRY * pEntry;
1434 URL_CACHEFILE_ENTRY * pUrlEntry;
1435 URLCACHECONTAINER * pContainer;
1437 TRACE("(%s, %p, %p, 0x%08lx)\n",
1438 debugstr_a(lpszUrlName),
1440 lpdwCacheEntryInfoBufferSize,
1443 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1446 if (!URLCacheContainer_OpenIndex(pContainer))
1449 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1452 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1454 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1455 TRACE("entry %s not found!\n", lpszUrlName);
1456 SetLastError(ERROR_FILE_NOT_FOUND);
1460 if (pEntry->dwSignature != URL_SIGNATURE)
1462 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1463 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1464 SetLastError(ERROR_FILE_NOT_FOUND);
1468 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1469 TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
1470 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1472 pUrlEntry->dwHitRate++;
1473 pUrlEntry->dwUseCount++;
1474 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1476 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1478 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1481 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1483 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1488 /***********************************************************************
1489 * UnlockUrlCacheEntryFileA (WININET.@)
1492 BOOL WINAPI UnlockUrlCacheEntryFileA(
1493 IN LPCSTR lpszUrlName,
1497 LPURLCACHE_HEADER pHeader;
1498 CACHEFILE_ENTRY * pEntry;
1499 URL_CACHEFILE_ENTRY * pUrlEntry;
1500 URLCACHECONTAINER * pContainer;
1502 TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName), dwReserved);
1506 ERR("dwReserved != 0\n");
1507 SetLastError(ERROR_INVALID_PARAMETER);
1511 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1514 if (!URLCacheContainer_OpenIndex(pContainer))
1517 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1520 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1522 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1523 TRACE("entry %s not found!\n", lpszUrlName);
1524 SetLastError(ERROR_FILE_NOT_FOUND);
1528 if (pEntry->dwSignature != URL_SIGNATURE)
1530 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1531 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1532 SetLastError(ERROR_FILE_NOT_FOUND);
1536 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1538 if (pUrlEntry->dwUseCount == 0)
1540 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1543 pUrlEntry->dwUseCount--;
1544 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1546 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1551 /***********************************************************************
1552 * CreateUrlCacheEntryA (WININET.@)
1555 BOOL WINAPI CreateUrlCacheEntryA(
1556 IN LPCSTR lpszUrlName,
1557 IN DWORD dwExpectedFileSize,
1558 IN LPCSTR lpszFileExtension,
1559 OUT LPSTR lpszFileName,
1563 URLCACHECONTAINER * pContainer;
1564 LPURLCACHE_HEADER pHeader;
1565 CHAR szFile[MAX_PATH];
1566 CHAR szExtension[MAX_PATH];
1569 LPCSTR lpszFileNameExtension;
1570 LPSTR lpszFileNameNoPath;
1572 int countnoextension;
1575 BOOL bFound = FALSE;
1578 TRACE("(%s, 0x%08lx, %s, %p, 0x%08lx)\n",
1579 debugstr_a(lpszUrlName),
1581 debugstr_a(lpszFileExtension),
1587 ERR("dwReserved != 0\n");
1588 SetLastError(ERROR_INVALID_PARAMETER);
1592 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1595 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1598 for (lpszUrlPart = lpszUrlEnd;
1599 (lpszUrlPart >= lpszUrlName);
1602 if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1609 if (!strcmp(lpszUrlPart, "www"))
1611 lpszUrlPart += strlen("www");
1614 count = lpszUrlEnd - lpszUrlPart;
1616 if (bFound && (count < MAX_PATH))
1618 memcpy(szFile, lpszUrlPart, count * sizeof(CHAR));
1619 szFile[count] = '\0';
1620 /* FIXME: get rid of illegal characters like \, / and : */
1624 FIXME("need to generate a random filename\n");
1627 TRACE("File name: %s\n", szFile);
1629 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1632 if (!URLCacheContainer_OpenIndex(pContainer))
1635 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1638 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1640 lBufferSize = MAX_PATH * sizeof(CHAR);
1641 URLCache_LocalFileNameToPathA(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1643 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1645 lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(CHAR) + DIR_LENGTH + 1;
1647 countnoextension = strlen(lpszFileNameNoPath);
1648 lpszFileNameExtension = PathFindExtensionA(lpszFileNameNoPath);
1649 if (lpszFileNameExtension)
1650 countnoextension -= strlen(lpszFileNameExtension);
1651 *szExtension = '\0';
1653 if (lpszFileExtension)
1655 szExtension[0] = '.';
1656 strcpy(szExtension+1, lpszFileExtension);
1659 for (i = 0; i < 255; i++)
1662 strncpy(lpszFileNameNoPath, szFile, countnoextension);
1663 sprintf(lpszFileNameNoPath + countnoextension, "[%u]%s", i, szExtension);
1664 TRACE("Trying: %s\n", lpszFileName);
1665 hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1666 if (hFile != INVALID_HANDLE_VALUE)
1676 /***********************************************************************
1677 * CommitUrlCacheEntryA (WININET.@)
1680 BOOL WINAPI CommitUrlCacheEntryA(
1681 IN LPCSTR lpszUrlName,
1682 IN LPCSTR lpszLocalFileName,
1683 IN FILETIME ExpireTime,
1684 IN FILETIME LastModifiedTime,
1685 IN DWORD CacheEntryType,
1686 IN LPBYTE lpHeaderInfo,
1687 IN DWORD dwHeaderSize,
1688 IN LPCSTR lpszFileExtension,
1689 IN LPCSTR dwReserved
1692 URLCACHECONTAINER * pContainer;
1693 LPURLCACHE_HEADER pHeader;
1694 CACHEFILE_ENTRY * pEntry;
1695 URL_CACHEFILE_ENTRY * pUrlEntry;
1696 DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1697 DWORD dwOffsetLocalFileName = 0;
1698 DWORD dwOffsetHeader = 0;
1699 DWORD dwFileSizeLow = 0;
1700 DWORD dwFileSizeHigh = 0;
1701 BYTE cDirectory = 0;
1703 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1704 debugstr_a(lpszUrlName),
1705 debugstr_a(lpszLocalFileName),
1714 ERR("dwReserved != 0\n");
1715 SetLastError(ERROR_INVALID_PARAMETER);
1718 if (lpHeaderInfo == NULL)
1720 FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1723 if (lpszLocalFileName)
1726 hFile = CreateFileA(lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1727 if (hFile == INVALID_HANDLE_VALUE)
1729 ERR("couldn't open file %s (error is %ld)\n", debugstr_a(lpszLocalFileName), GetLastError());
1734 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1735 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
1737 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1745 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1748 if (!URLCacheContainer_OpenIndex(pContainer))
1751 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1754 if (URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1756 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1757 FIXME("entry already in cache - don't know what to do!\n");
1759 * SetLastError(ERROR_FILE_NOT_FOUND);
1765 if (lpszLocalFileName)
1767 BOOL bFound = FALSE;
1768 char szContainerPath[MAX_PATH];
1769 int container_path_len;
1770 container_path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szContainerPath, sizeof(szContainerPath), NULL, NULL);
1771 if (!container_path_len)
1773 /* WideCharToMultiByte should have called SetLastError */
1777 if (strncmp(lpszLocalFileName, szContainerPath, container_path_len))
1779 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1780 ERR("path %s must begin with cache content path %s\n", debugstr_a(lpszLocalFileName), debugstr_a(szContainerPath));
1781 SetLastError(ERROR_INVALID_PARAMETER);
1785 /* skip container path prefix */
1786 lpszLocalFileName += container_path_len;
1788 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1790 if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1799 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1800 ERR("cache directory not found in path %s\n", lpszLocalFileName);
1801 SetLastError(ERROR_INVALID_PARAMETER);
1805 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1808 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlName) + 1);
1809 if (lpszLocalFileName)
1811 dwOffsetLocalFileName = dwBytesNeeded;
1812 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszLocalFileName) + 1);
1816 dwOffsetHeader = dwBytesNeeded;
1817 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1820 /* round up to next block */
1821 if (dwBytesNeeded % BLOCKSIZE)
1823 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1824 dwBytesNeeded += BLOCKSIZE;
1827 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1829 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1830 ERR("no free entries\n");
1834 /* FindFirstFreeEntry fills in blocks used */
1835 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1836 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1837 pUrlEntry->CacheDir = cDirectory;
1838 pUrlEntry->CacheEntryType = CacheEntryType;
1839 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1840 pUrlEntry->dwExemptDelta = 0;
1841 pUrlEntry->dwHitRate = 0;
1842 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1843 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1844 pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1845 pUrlEntry->dwSizeHigh = 0;
1846 pUrlEntry->dwSizeLow = dwFileSizeLow;
1847 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1848 pUrlEntry->dwUseCount = 0;
1849 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1850 pUrlEntry->LastModifiedTime = LastModifiedTime;
1851 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1852 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1853 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1854 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1857 pUrlEntry->dwUnknown1 = 0;
1858 pUrlEntry->dwUnknown2 = 0;
1859 pUrlEntry->dwUnknown3 = 0x60;
1860 pUrlEntry->Unknown4 = 0;
1861 pUrlEntry->wUnknown5 = 0x1010;
1862 pUrlEntry->dwUnknown6 = 0;
1863 pUrlEntry->dwUnknown7 = 0;
1864 pUrlEntry->dwUnknown8 = 0;
1866 strcpy(pUrlEntry->szSourceUrlName, lpszUrlName);
1867 if (dwOffsetLocalFileName)
1868 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), lpszLocalFileName);
1870 memcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetHeader), lpHeaderInfo, dwHeaderSize);
1872 if (!URLCache_AddEntryToHash(pHeader, lpszUrlName, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1874 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1878 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1883 BOOL WINAPI ReadUrlCacheEntryStream(
1884 IN HANDLE hUrlCacheStream,
1885 IN DWORD dwLocation,
1886 IN OUT LPVOID lpBuffer,
1887 IN OUT LPDWORD lpdwLen,
1891 /* Get handle to file from 'stream' */
1892 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1894 if (dwReserved != 0)
1896 ERR("dwReserved != 0\n");
1897 SetLastError(ERROR_INVALID_PARAMETER);
1901 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1903 SetLastError(ERROR_INVALID_HANDLE);
1907 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
1909 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
1912 /***********************************************************************
1913 * RetrieveUrlCacheEntryStreamA (WININET.@)
1916 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
1917 IN LPCSTR lpszUrlName,
1918 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1919 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1920 IN BOOL fRandomRead,
1924 /* NOTE: this is not the same as the way that the native
1925 * version allocates 'stream' handles. I did it this way
1926 * as it is much easier and no applications should depend
1927 * on this behaviour. (Native version appears to allocate
1928 * indices into a table)
1930 STREAM_HANDLE * pStream;
1933 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
1935 lpdwCacheEntryInfoBufferSize,
1941 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1946 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
1948 if (hFile == INVALID_HANDLE_VALUE)
1951 /* allocate handle storage space */
1952 pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
1956 SetLastError(ERROR_OUTOFMEMORY);
1960 pStream->hFile = hFile;
1961 strcpy(pStream->lpszUrl, lpszUrlName);
1962 return (HANDLE)pStream;
1965 /***********************************************************************
1966 * UnlockUrlCacheEntryStream (WININET.@)
1969 BOOL WINAPI UnlockUrlCacheEntryStream(
1970 IN HANDLE hUrlCacheStream,
1974 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1976 if (dwReserved != 0)
1978 ERR("dwReserved != 0\n");
1979 SetLastError(ERROR_INVALID_PARAMETER);
1983 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1985 SetLastError(ERROR_INVALID_HANDLE);
1989 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
1992 /* close file handle */
1993 CloseHandle(pStream->hFile);
1995 /* free allocated space */
1996 HeapFree(GetProcessHeap(), 0, pStream);
2002 /***********************************************************************
2003 * DeleteUrlCacheEntryA (WININET.@)
2006 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2008 URLCACHECONTAINER * pContainer;
2009 LPURLCACHE_HEADER pHeader;
2010 CACHEFILE_ENTRY * pEntry;
2013 BYTE * AllocationTable;
2015 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2017 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2020 if (!URLCacheContainer_OpenIndex(pContainer))
2023 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2026 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2028 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2029 TRACE("entry %s not found!\n", lpszUrlName);
2030 SetLastError(ERROR_FILE_NOT_FOUND);
2034 AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
2036 /* update allocation table */
2037 dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
2038 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
2039 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
2041 URLCache_DeleteEntry(pEntry);
2043 URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2045 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2050 /***********************************************************************
2051 * FindFirstUrlCacheEntryA (WININET.@)
2054 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2055 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2057 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2058 SetLastError(ERROR_FILE_NOT_FOUND);
2062 /***********************************************************************
2063 * FindFirstUrlCacheEntryW (WININET.@)
2066 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2067 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2069 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2073 /***********************************************************************
2074 * CreateUrlCacheGroup (WININET.@)
2077 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID
2080 FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
2084 /***********************************************************************
2085 * DeleteUrlCacheGroup (WININET.@)
2088 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2094 /***********************************************************************
2095 * SetUrlCacheEntryGroup (WININET.@)
2098 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
2099 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2103 SetLastError(ERROR_FILE_NOT_FOUND);
2107 /***********************************************************************
2108 * GetUrlCacheConfigInfoW (WININET.@)
2110 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2112 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2113 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2117 /***********************************************************************
2118 * GetUrlCacheConfigInfoA (WININET.@)
2120 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2122 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2124 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2125 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2129 /***********************************************************************
2130 * DeleteIE3Cache (WININET.@)
2132 * Deletes the files used by the IE3 URL caching system.
2135 * hWnd [I] A dummy window.
2136 * hInst [I] Instance of process calling the function.
2137 * lpszCmdLine [I] Options used by function.
2138 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
2143 void WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2145 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);