ntdll: Expand previous patch to use pthread_mutex
[wine/multimedia.git] / dlls / wininet / urlcache.c
blobe958d001ecced96222a3a014bba2bf5516525dfe
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
32 #include <ws2tcpip.h>
33 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #include <time.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wininet.h"
49 #include "winineti.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "shlwapi.h"
53 #include "shlobj.h"
54 #include "shellapi.h"
56 #include "internet.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define ENTRY_START_OFFSET 0x4000
64 #define DIR_LENGTH 8
65 #define BLOCKSIZE 128
66 #define HASHTABLE_SIZE 448
67 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
68 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
71 #define MIN_BLOCK_NO 0x80
72 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
73 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
75 #define HASHTABLE_URL 0
76 #define HASHTABLE_DEL 1
77 #define HASHTABLE_LOCK 2
78 #define HASHTABLE_FREE 3
79 #define HASHTABLE_REDR 5
80 #define HASHTABLE_FLAG_BITS 5
82 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
83 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
84 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
85 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
86 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
88 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
90 typedef struct _CACHEFILE_ENTRY
92 /* union
93 {*/
94 DWORD dwSignature; /* e.g. "URL " */
95 /* CHAR szSignature[4];
96 };*/
97 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
98 } CACHEFILE_ENTRY;
100 typedef struct _URL_CACHEFILE_ENTRY
102 CACHEFILE_ENTRY CacheFileEntry;
103 FILETIME LastModifiedTime;
104 FILETIME LastAccessTime;
105 WORD wExpiredDate; /* expire date in dos format */
106 WORD wExpiredTime; /* expire time in dos format */
107 DWORD dwUnknown1; /* usually zero */
108 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
109 DWORD dwUnknown2; /* usually zero */
110 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
111 DWORD dwUnknown3; /* usually 0x60 */
112 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
113 BYTE CacheDir; /* index of cache directory this url is stored in */
114 BYTE Unknown4; /* usually zero */
115 WORD wUnknown5; /* usually 0x1010 */
116 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
117 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
118 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
119 DWORD dwHeaderInfoSize;
120 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
121 WORD wLastSyncDate; /* last sync date in dos format */
122 WORD wLastSyncTime; /* last sync time in dos format */
123 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
124 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
125 WORD wUnknownDate; /* usually same as wLastSyncDate */
126 WORD wUnknownTime; /* usually same as wLastSyncTime */
127 DWORD dwUnknown7; /* usually zero */
128 DWORD dwUnknown8; /* usually zero */
129 /* packing to dword align start of next field */
130 /* CHAR szSourceUrlName[]; (url) */
131 /* packing to dword align start of next field */
132 /* CHAR szLocalFileName[]; (local file name excluding path) */
133 /* packing to dword align start of next field */
134 /* CHAR szHeaderInfo[]; (header info) */
135 } URL_CACHEFILE_ENTRY;
137 struct _HASH_ENTRY
139 DWORD dwHashKey;
140 DWORD dwOffsetEntry;
143 typedef struct _HASH_CACHEFILE_ENTRY
145 CACHEFILE_ENTRY CacheFileEntry;
146 DWORD dwAddressNext;
147 DWORD dwHashTableNumber;
148 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
149 } HASH_CACHEFILE_ENTRY;
151 typedef struct _DIRECTORY_DATA
153 DWORD dwNumFiles;
154 char filename[DIR_LENGTH];
155 } DIRECTORY_DATA;
157 typedef struct _URLCACHE_HEADER
159 char szSignature[28];
160 DWORD dwFileSize;
161 DWORD dwOffsetFirstHashTable;
162 DWORD dwIndexCapacityInBlocks;
163 DWORD dwBlocksInUse;
164 DWORD dwUnknown1;
165 ULARGE_INTEGER CacheLimit;
166 ULARGE_INTEGER CacheUsage;
167 ULARGE_INTEGER ExemptUsage;
168 DWORD DirectoryCount; /* number of directory_data's */
169 DIRECTORY_DATA directory_data[1]; /* first directory entry */
170 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
171 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
173 typedef struct _STREAM_HANDLE
175 HANDLE hFile;
176 CHAR lpszUrl[1];
177 } STREAM_HANDLE;
179 typedef struct _URLCACHECONTAINER
181 struct list entry; /* part of a list */
182 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
183 LPWSTR path; /* path to url container directory */
184 HANDLE hMapping; /* handle of file mapping */
185 DWORD file_size; /* size of file when mapping was opened */
186 HANDLE hMutex; /* handle of mutex */
187 } URLCACHECONTAINER;
190 /* List of all containers available */
191 static struct list UrlContainers = LIST_INIT(UrlContainers);
193 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
195 /***********************************************************************
196 * URLCache_PathToObjectName (Internal)
198 * Converts a path to a name suitable for use as a Win32 object name.
199 * Replaces '\\' characters in-place with the specified character
200 * (usually '_' or '!')
202 * RETURNS
203 * nothing
206 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
208 for (; *lpszPath; lpszPath++)
210 if (*lpszPath == '\\')
211 *lpszPath = replace;
215 /***********************************************************************
216 * URLCacheContainer_OpenIndex (Internal)
218 * Opens the index file and saves mapping handle in hCacheIndexMapping
220 * RETURNS
221 * ERROR_SUCCESS if succeeded
222 * Any other Win32 error code if failed
225 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer, DWORD blocks_no)
227 HANDLE hFile;
228 WCHAR wszFilePath[MAX_PATH];
229 DWORD dwFileSize, new_file_size;
231 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
232 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
234 WaitForSingleObject(pContainer->hMutex, INFINITE);
236 if (pContainer->hMapping) {
237 ReleaseMutex(pContainer->hMutex);
238 return ERROR_SUCCESS;
241 strcpyW(wszFilePath, pContainer->path);
242 strcatW(wszFilePath, wszIndex);
244 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
245 if (hFile == INVALID_HANDLE_VALUE)
247 /* Maybe the directory wasn't there? Try to create it */
248 if (CreateDirectoryW(pContainer->path, 0))
249 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
251 if (hFile == INVALID_HANDLE_VALUE)
253 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
254 ReleaseMutex(pContainer->hMutex);
255 return GetLastError();
258 dwFileSize = GetFileSize(hFile, NULL);
259 if (dwFileSize == INVALID_FILE_SIZE)
261 ReleaseMutex(pContainer->hMutex);
262 return GetLastError();
265 if (blocks_no < MIN_BLOCK_NO)
266 blocks_no = MIN_BLOCK_NO;
267 else if (blocks_no > MAX_BLOCK_NO)
268 blocks_no = MAX_BLOCK_NO;
269 new_file_size = FILE_SIZE(blocks_no);
271 if (dwFileSize < new_file_size)
273 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
274 HKEY key;
275 char achZeroes[0x1000];
276 DWORD dwOffset;
277 DWORD dwError = ERROR_SUCCESS;
279 if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
280 dwError = GetLastError();
282 /* Write zeroes to the entire file so we can safely map it without
283 * fear of getting a SEGV because the disk is full.
285 memset(achZeroes, 0, sizeof(achZeroes));
286 for (dwOffset = dwFileSize; dwOffset<new_file_size && dwError==ERROR_SUCCESS;
287 dwOffset += sizeof(achZeroes))
289 DWORD dwWrite = sizeof(achZeroes);
290 DWORD dwWritten;
292 if (new_file_size - dwOffset < dwWrite)
293 dwWrite = new_file_size - dwOffset;
294 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
295 dwWritten != dwWrite)
297 /* If we fail to write, we need to return the error that
298 * cause the problem and also make sure the file is no
299 * longer there, if possible.
301 dwError = GetLastError();
305 if (dwError == ERROR_SUCCESS)
307 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
309 if (hMapping)
311 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
313 if (pHeader && dwFileSize)
315 pHeader->dwFileSize = new_file_size;
316 pHeader->dwIndexCapacityInBlocks = blocks_no;
318 else if (pHeader)
320 WCHAR *pwchDir;
321 WCHAR wszDirPath[MAX_PATH];
322 FILETIME ft;
323 int i, j;
324 HASH_CACHEFILE_ENTRY *pHashEntry;
326 /* First set some constants and defaults in the header */
327 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
328 pHeader->dwFileSize = new_file_size;
329 pHeader->dwIndexCapacityInBlocks = blocks_no;
330 /* 127MB - taken from default for Windows 2000 */
331 pHeader->CacheLimit.QuadPart = 0x07ff5400;
332 /* Copied from a Windows 2000 cache index */
333 pHeader->DirectoryCount = 4;
335 /* If the registry has a cache size set, use the registry value */
336 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
338 DWORD dw;
339 DWORD len = sizeof(dw);
340 DWORD keytype;
342 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
343 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
344 keytype == REG_DWORD)
346 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
348 RegCloseKey(key);
351 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
353 /* Last step - create the directories */
355 strcpyW(wszDirPath, pContainer->path);
356 pwchDir = wszDirPath + strlenW(wszDirPath);
357 pwchDir[8] = 0;
359 GetSystemTimeAsFileTime(&ft);
361 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
363 pHeader->directory_data[i].dwNumFiles = 0;
364 for (j = 0;; ++j)
366 int k;
367 ULONGLONG n = ft.dwHighDateTime;
369 /* Generate a file name to attempt to create.
370 * This algorithm will create what will appear
371 * to be random and unrelated directory names
372 * of up to 9 characters in length.
374 n <<= 32;
375 n += ft.dwLowDateTime;
376 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
378 for (k = 0; k < 8; ++k)
380 int r = (n % 36);
382 /* Dividing by a prime greater than 36 helps
383 * with the appearance of randomness
385 n /= 37;
387 if (r < 10)
388 pwchDir[k] = '0' + r;
389 else
390 pwchDir[k] = 'A' + (r - 10);
393 if (CreateDirectoryW(wszDirPath, 0))
395 /* The following is OK because we generated an
396 * 8 character directory name made from characters
397 * [A-Z0-9], which are equivalent for all code
398 * pages and for UTF-16
400 for (k = 0; k < 8; ++k)
401 pHeader->directory_data[i].filename[k] = pwchDir[k];
402 break;
404 else if (j >= 255)
406 /* Give up. The most likely cause of this
407 * is a full disk, but whatever the cause
408 * is, it should be more than apparent that
409 * we won't succeed.
411 dwError = GetLastError();
412 break;
417 UnmapViewOfFile(pHeader);
419 else
421 dwError = GetLastError();
423 dwFileSize = new_file_size;
424 CloseHandle(hMapping);
426 else
428 dwError = GetLastError();
432 if (dwError)
434 CloseHandle(hFile);
435 DeleteFileW(wszFilePath);
436 ReleaseMutex(pContainer->hMutex);
437 return dwError;
442 pContainer->file_size = dwFileSize;
443 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
444 URLCache_PathToObjectName(wszFilePath, '_');
445 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
446 if (!pContainer->hMapping)
447 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
448 CloseHandle(hFile);
449 if (!pContainer->hMapping)
451 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
452 ReleaseMutex(pContainer->hMutex);
453 return GetLastError();
456 ReleaseMutex(pContainer->hMutex);
458 return ERROR_SUCCESS;
461 /***********************************************************************
462 * URLCacheContainer_CloseIndex (Internal)
464 * Closes the index
466 * RETURNS
467 * nothing
470 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
472 CloseHandle(pContainer->hMapping);
473 pContainer->hMapping = NULL;
476 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
478 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
479 int cache_prefix_len = strlenW(cache_prefix);
481 if (!pContainer)
483 return FALSE;
486 pContainer->hMapping = NULL;
487 pContainer->file_size = 0;
489 pContainer->path = heap_strdupW(path);
490 if (!pContainer->path)
492 heap_free(pContainer);
493 return FALSE;
496 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
497 if (!pContainer->cache_prefix)
499 heap_free(pContainer->path);
500 heap_free(pContainer);
501 return FALSE;
504 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
506 CharLowerW(mutex_name);
507 URLCache_PathToObjectName(mutex_name, '!');
509 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
511 ERR("couldn't create mutex (error is %d)\n", GetLastError());
512 heap_free(pContainer->path);
513 heap_free(pContainer);
514 return FALSE;
517 list_add_head(&UrlContainers, &pContainer->entry);
519 return TRUE;
522 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
524 list_remove(&pContainer->entry);
526 URLCacheContainer_CloseIndex(pContainer);
527 CloseHandle(pContainer->hMutex);
528 heap_free(pContainer->path);
529 heap_free(pContainer->cache_prefix);
530 heap_free(pContainer);
533 void URLCacheContainers_CreateDefaults(void)
535 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
536 static const WCHAR UrlPrefix[] = {0};
537 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
538 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
539 static const WCHAR CookieSuffix[] = {0};
540 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
541 static const struct
543 int nFolder; /* CSIDL_* constant */
544 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
545 const WCHAR * cache_prefix; /* prefix used to reference the container */
546 } DefaultContainerData[] =
548 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
549 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
550 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
552 DWORD i;
554 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
556 WCHAR wszCachePath[MAX_PATH];
557 WCHAR wszMutexName[MAX_PATH];
558 int path_len, suffix_len;
560 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
562 ERR("Couldn't get path for default container %u\n", i);
563 continue;
565 path_len = strlenW(wszCachePath);
566 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
568 if (path_len + suffix_len + 2 > MAX_PATH)
570 ERR("Path too long\n");
571 continue;
574 wszCachePath[path_len] = '\\';
575 wszCachePath[path_len+1] = 0;
577 strcpyW(wszMutexName, wszCachePath);
579 if (suffix_len)
581 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
582 wszCachePath[path_len + suffix_len + 1] = '\\';
583 wszCachePath[path_len + suffix_len + 2] = '\0';
586 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
590 void URLCacheContainers_DeleteAll(void)
592 while(!list_empty(&UrlContainers))
593 URLCacheContainer_DeleteContainer(
594 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
598 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
600 URLCACHECONTAINER * pContainer;
602 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
604 if(!lpwszUrl)
605 return ERROR_INVALID_PARAMETER;
607 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
609 int prefix_len = strlenW(pContainer->cache_prefix);
610 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
612 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
613 *ppContainer = pContainer;
614 return ERROR_SUCCESS;
617 ERR("no container found\n");
618 return ERROR_FILE_NOT_FOUND;
621 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
623 LPWSTR url = NULL;
624 DWORD ret;
626 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
627 return ERROR_OUTOFMEMORY;
629 ret = URLCacheContainers_FindContainerW(url, ppContainer);
630 heap_free(url);
631 return ret;
634 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
636 DWORD i = 0;
637 URLCACHECONTAINER * pContainer;
639 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
641 /* non-NULL search pattern only returns one container ever */
642 if (lpwszSearchPattern && dwIndex > 0)
643 return FALSE;
645 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
647 if (lpwszSearchPattern)
649 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
651 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
652 *ppContainer = pContainer;
653 return TRUE;
656 else
658 if (i == dwIndex)
660 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
661 *ppContainer = pContainer;
662 return TRUE;
665 i++;
667 return FALSE;
670 /***********************************************************************
671 * URLCacheContainer_LockIndex (Internal)
673 * Locks the index for system-wide exclusive access.
675 * RETURNS
676 * Cache file header if successful
677 * NULL if failed and calls SetLastError.
679 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
681 BYTE index;
682 LPVOID pIndexData;
683 URLCACHE_HEADER * pHeader;
684 DWORD error;
686 /* acquire mutex */
687 WaitForSingleObject(pContainer->hMutex, INFINITE);
689 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
691 if (!pIndexData)
693 ReleaseMutex(pContainer->hMutex);
694 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
695 return NULL;
697 pHeader = (URLCACHE_HEADER *)pIndexData;
699 /* file has grown - we need to remap to prevent us getting
700 * access violations when we try and access beyond the end
701 * of the memory mapped file */
702 if (pHeader->dwFileSize != pContainer->file_size)
704 UnmapViewOfFile( pHeader );
705 URLCacheContainer_CloseIndex(pContainer);
706 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
707 if (error != ERROR_SUCCESS)
709 ReleaseMutex(pContainer->hMutex);
710 SetLastError(error);
711 return NULL;
713 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
715 if (!pIndexData)
717 ReleaseMutex(pContainer->hMutex);
718 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
719 return NULL;
721 pHeader = (URLCACHE_HEADER *)pIndexData;
724 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
726 for (index = 0; index < pHeader->DirectoryCount; index++)
728 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
731 return pHeader;
734 /***********************************************************************
735 * URLCacheContainer_UnlockIndex (Internal)
738 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
740 /* release mutex */
741 ReleaseMutex(pContainer->hMutex);
742 return UnmapViewOfFile(pHeader);
745 /***********************************************************************
746 * URLCacheContainer_CleanIndex (Internal)
748 * This function is meant to make place in index file by removing old
749 * entries and resizing the file.
751 * CAUTION: file view may get mapped to new memory
752 * TODO: implement entries cleaning
754 * RETURNS
755 * ERROR_SUCCESS when new memory is available
756 * error code otherwise
758 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
760 URLCACHE_HEADER *header = *file_view;
761 DWORD ret;
763 FIXME("(%s %s) semi-stub\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
765 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
766 WARN("index file has maximal size\n");
767 return ERROR_NOT_ENOUGH_MEMORY;
770 URLCacheContainer_CloseIndex(container);
771 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
772 if(ret != ERROR_SUCCESS)
773 return ret;
774 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
775 if(!header)
776 return GetLastError();
778 UnmapViewOfFile(*file_view);
779 *file_view = header;
780 return ERROR_SUCCESS;
783 #ifndef CHAR_BIT
784 #define CHAR_BIT (8 * sizeof(CHAR))
785 #endif
787 /***********************************************************************
788 * URLCache_Allocation_BlockIsFree (Internal)
790 * Is the specified block number free?
792 * RETURNS
793 * zero if free
794 * non-zero otherwise
797 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
799 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
800 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
803 /***********************************************************************
804 * URLCache_Allocation_BlockFree (Internal)
806 * Marks the specified block as free
808 * RETURNS
809 * nothing
812 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
814 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
815 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
818 /***********************************************************************
819 * URLCache_Allocation_BlockAllocate (Internal)
821 * Marks the specified block as allocated
823 * RETURNS
824 * nothing
827 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
829 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
830 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
833 /***********************************************************************
834 * URLCache_FindFirstFreeEntry (Internal)
836 * Finds and allocates the first block of free space big enough and
837 * sets ppEntry to point to it.
839 * RETURNS
840 * ERROR_SUCCESS when free memory block was found
841 * Any other Win32 error code if the entry could not be added
844 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
846 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
847 DWORD dwBlockNumber;
848 DWORD dwFreeCounter;
849 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
851 for (dwFreeCounter = 0;
852 dwFreeCounter < dwBlocksNeeded &&
853 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
854 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
855 dwFreeCounter++)
856 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
858 if (dwFreeCounter == dwBlocksNeeded)
860 DWORD index;
861 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
862 for (index = 0; index < dwBlocksNeeded; index++)
863 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
864 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
865 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
866 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
867 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
868 return ERROR_SUCCESS;
872 return ERROR_HANDLE_DISK_FULL;
875 /***********************************************************************
876 * URLCache_DeleteEntry (Internal)
878 * Deletes the specified entry and frees the space allocated to it
880 * RETURNS
881 * TRUE if it succeeded
882 * FALSE if it failed
885 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
887 DWORD dwStartBlock;
888 DWORD dwBlock;
889 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
891 /* update allocation table */
892 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
893 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
894 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
896 return TRUE;
899 /***********************************************************************
900 * URLCache_LocalFileNameToPathW (Internal)
902 * Copies the full path to the specified buffer given the local file
903 * name and the index of the directory it is in. Always sets value in
904 * lpBufferSize to the required buffer size (in bytes).
906 * RETURNS
907 * TRUE if the buffer was big enough
908 * FALSE if the buffer was too small
911 static BOOL URLCache_LocalFileNameToPathW(
912 const URLCACHECONTAINER * pContainer,
913 LPCURLCACHE_HEADER pHeader,
914 LPCSTR szLocalFileName,
915 BYTE Directory,
916 LPWSTR wszPath,
917 LPLONG lpBufferSize)
919 LONG nRequired;
920 int path_len = strlenW(pContainer->path);
921 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
922 if (Directory >= pHeader->DirectoryCount)
924 *lpBufferSize = 0;
925 return FALSE;
928 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
929 if (nRequired <= *lpBufferSize)
931 int dir_len;
933 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
934 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
935 wszPath[dir_len + path_len] = '\\';
936 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
937 *lpBufferSize = nRequired;
938 return TRUE;
940 *lpBufferSize = nRequired;
941 return FALSE;
944 /***********************************************************************
945 * URLCache_LocalFileNameToPathA (Internal)
947 * Copies the full path to the specified buffer given the local file
948 * name and the index of the directory it is in. Always sets value in
949 * lpBufferSize to the required buffer size.
951 * RETURNS
952 * TRUE if the buffer was big enough
953 * FALSE if the buffer was too small
956 static BOOL URLCache_LocalFileNameToPathA(
957 const URLCACHECONTAINER * pContainer,
958 LPCURLCACHE_HEADER pHeader,
959 LPCSTR szLocalFileName,
960 BYTE Directory,
961 LPSTR szPath,
962 LPLONG lpBufferSize)
964 LONG nRequired;
965 int path_len, file_name_len, dir_len;
967 if (Directory >= pHeader->DirectoryCount)
969 *lpBufferSize = 0;
970 return FALSE;
973 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
974 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
975 dir_len = DIR_LENGTH;
977 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
978 if (nRequired < *lpBufferSize)
980 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
981 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
982 szPath[path_len + dir_len] = '\\';
983 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
984 *lpBufferSize = nRequired;
985 return TRUE;
987 *lpBufferSize = nRequired;
988 return FALSE;
991 /* Just like DosDateTimeToFileTime, except that it also maps the special
992 * case of a DOS date/time of (0,0) to a filetime of (0,0).
994 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
995 FILETIME *ft)
997 if (!fatdate && !fattime)
998 ft->dwLowDateTime = ft->dwHighDateTime = 0;
999 else
1000 DosDateTimeToFileTime(fatdate, fattime, ft);
1003 /***********************************************************************
1004 * URLCache_CopyEntry (Internal)
1006 * Copies an entry from the cache index file to the Win32 structure
1008 * RETURNS
1009 * ERROR_SUCCESS if the buffer was big enough
1010 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1013 static DWORD URLCache_CopyEntry(
1014 URLCACHECONTAINER * pContainer,
1015 LPCURLCACHE_HEADER pHeader,
1016 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1017 LPDWORD lpdwBufferSize,
1018 const URL_CACHEFILE_ENTRY * pUrlEntry,
1019 BOOL bUnicode)
1021 int lenUrl;
1022 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1024 if (*lpdwBufferSize >= dwRequiredSize)
1026 lpCacheEntryInfo->lpHeaderInfo = NULL;
1027 lpCacheEntryInfo->lpszFileExtension = NULL;
1028 lpCacheEntryInfo->lpszLocalFileName = NULL;
1029 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1030 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1031 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1032 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1033 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1034 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1035 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1036 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1037 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1038 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1039 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1040 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1041 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1042 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1043 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1046 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1047 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1048 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1049 if (bUnicode)
1050 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1051 else
1052 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1053 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1055 /* FIXME: is source url optional? */
1056 if (*lpdwBufferSize >= dwRequiredSize)
1058 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1060 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1061 if (bUnicode)
1062 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1063 else
1064 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1067 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1068 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1069 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1071 if (pUrlEntry->dwOffsetLocalName)
1073 LONG nLocalFilePathSize;
1074 LPSTR lpszLocalFileName;
1075 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1076 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1077 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1078 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1080 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1082 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1084 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1085 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1086 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1088 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1090 if (*lpdwBufferSize >= dwRequiredSize)
1092 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1093 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1094 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1096 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1097 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1098 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1100 if (pUrlEntry->dwOffsetFileExtension)
1102 int lenExtension;
1104 if (bUnicode)
1105 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1106 else
1107 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1108 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1110 if (*lpdwBufferSize >= dwRequiredSize)
1112 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1113 if (bUnicode)
1114 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1115 else
1116 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1119 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1120 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1121 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1124 if (dwRequiredSize > *lpdwBufferSize)
1126 *lpdwBufferSize = dwRequiredSize;
1127 return ERROR_INSUFFICIENT_BUFFER;
1129 *lpdwBufferSize = dwRequiredSize;
1130 return ERROR_SUCCESS;
1133 /* Just like FileTimeToDosDateTime, except that it also maps the special
1134 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1136 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1137 WORD *fattime)
1139 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1140 *fatdate = *fattime = 0;
1141 else
1142 FileTimeToDosDateTime(ft, fatdate, fattime);
1145 /***********************************************************************
1146 * URLCache_SetEntryInfo (Internal)
1148 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1149 * according to the flags set by dwFieldControl.
1151 * RETURNS
1152 * ERROR_SUCCESS if the buffer was big enough
1153 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1156 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1158 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1159 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1160 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1161 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1162 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1163 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1164 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1165 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1166 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1167 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1168 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1169 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1170 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1171 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1172 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1173 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1175 return ERROR_SUCCESS;
1178 /***********************************************************************
1179 * URLCache_HashKey (Internal)
1181 * Returns the hash key for a given string
1183 * RETURNS
1184 * hash key for the string
1187 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1189 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1190 * but the algorithm and result are not the same!
1192 static const unsigned char lookupTable[256] =
1194 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1195 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1196 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1197 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1198 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1199 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1200 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1201 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1202 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1203 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1204 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1205 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1206 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1207 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1208 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1209 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1210 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1211 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1212 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1213 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1214 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1215 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1216 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1217 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1218 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1219 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1220 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1221 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1222 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1223 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1224 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1225 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1227 BYTE key[4];
1228 DWORD i;
1230 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1231 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1233 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1235 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1236 key[i] = lookupTable[*lpszKey ^ key[i]];
1239 return *(DWORD *)key;
1242 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1244 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1247 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1249 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1250 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1251 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1254 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1256 /* structure of hash table:
1257 * 448 entries divided into 64 blocks
1258 * each block therefore contains a chain of 7 key/offset pairs
1259 * how position in table is calculated:
1260 * 1. the url is hashed in helper function
1261 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1262 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1264 * note:
1265 * there can be multiple hash tables in the file and the offset to
1266 * the next one is stored in the header of the hash table
1268 DWORD key = URLCache_HashKey(lpszUrl);
1269 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1270 HASH_CACHEFILE_ENTRY * pHashEntry;
1271 DWORD dwHashTableNumber = 0;
1273 key >>= HASHTABLE_FLAG_BITS;
1275 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1276 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1277 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1279 int i;
1280 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1282 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1283 continue;
1285 /* make sure that it is in fact a hash entry */
1286 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1288 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1289 continue;
1292 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1294 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1295 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1297 /* FIXME: we should make sure that this is the right element
1298 * before returning and claiming that it is. We can do this
1299 * by doing a simple compare between the URL we were given
1300 * and the URL stored in the entry. However, this assumes
1301 * we know the format of all the entries stored in the
1302 * hash table */
1303 *ppHashEntry = pHashElement;
1304 return TRUE;
1308 return FALSE;
1311 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1313 LPSTR urlA;
1314 BOOL ret;
1316 urlA = heap_strdupWtoA(lpszUrl);
1317 if (!urlA)
1319 SetLastError(ERROR_OUTOFMEMORY);
1320 return FALSE;
1323 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1324 heap_free(urlA);
1325 return ret;
1328 /***********************************************************************
1329 * URLCache_HashEntrySetFlags (Internal)
1331 * Sets special bits in hash key
1333 * RETURNS
1334 * nothing
1337 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1339 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1342 /***********************************************************************
1343 * URLCache_DeleteEntryFromHash (Internal)
1345 * Searches all the hash tables in the index for the given URL and
1346 * then if found deletes the entry.
1348 * RETURNS
1349 * TRUE if the entry was found
1350 * FALSE if the entry could not be found
1353 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1355 pHashEntry->dwHashKey = HASHTABLE_DEL;
1356 return TRUE;
1359 /***********************************************************************
1360 * URLCache_AddEntryToHash (Internal)
1362 * Searches all the hash tables for a free slot based on the offset
1363 * generated from the hash key. If a free slot is found, the offset and
1364 * key are entered into the hash table.
1366 * RETURNS
1367 * ERROR_SUCCESS if the entry was added
1368 * Any other Win32 error code if the entry could not be added
1371 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1373 /* see URLCache_FindEntryInHash for structure of hash tables */
1375 DWORD key = URLCache_HashKey(lpszUrl);
1376 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1377 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1378 DWORD dwHashTableNumber = 0;
1379 DWORD error;
1381 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1383 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1384 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1385 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1387 int i;
1388 pHashPrev = pHashEntry;
1390 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1392 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1393 break;
1395 /* make sure that it is in fact a hash entry */
1396 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1398 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1399 break;
1402 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1404 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1405 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1407 pHashElement->dwHashKey = key;
1408 pHashElement->dwOffsetEntry = dwOffsetEntry;
1409 return ERROR_SUCCESS;
1413 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1414 if (error != ERROR_SUCCESS)
1415 return error;
1417 pHashEntry->HashTable[offset].dwHashKey = key;
1418 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1419 return ERROR_SUCCESS;
1422 /***********************************************************************
1423 * URLCache_CreateHashTable (Internal)
1425 * Creates a new hash table in free space and adds it to the chain of existing
1426 * hash tables.
1428 * RETURNS
1429 * ERROR_SUCCESS if the hash table was created
1430 * ERROR_DISK_FULL if the hash table could not be created
1433 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1435 DWORD dwOffset, error;
1436 int i;
1438 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1439 return error;
1441 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1443 if (pPrevHash)
1444 pPrevHash->dwAddressNext = dwOffset;
1445 else
1446 pHeader->dwOffsetFirstHashTable = dwOffset;
1447 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1448 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1449 (*ppHash)->dwAddressNext = 0;
1450 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1451 for (i = 0; i < HASHTABLE_SIZE; i++)
1453 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1454 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1456 return ERROR_SUCCESS;
1459 /***********************************************************************
1460 * URLCache_EnumHashTables (Internal)
1462 * Enumerates the hash tables in a container.
1464 * RETURNS
1465 * TRUE if an entry was found
1466 * FALSE if there are no more tables to enumerate.
1469 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1471 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1472 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1473 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1475 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1476 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1477 continue;
1478 /* make sure that it is in fact a hash entry */
1479 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1481 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1482 (*pdwHashTableNumber)++;
1483 continue;
1486 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1487 return TRUE;
1489 return FALSE;
1492 /***********************************************************************
1493 * URLCache_EnumHashTableEntries (Internal)
1495 * Enumerates entries in a hash table and returns the next non-free entry.
1497 * RETURNS
1498 * TRUE if an entry was found
1499 * FALSE if the hash table is empty or there are no more entries to
1500 * enumerate.
1503 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1504 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1506 for (; *index < HASHTABLE_SIZE ; (*index)++)
1508 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1509 continue;
1511 *ppHashEntry = &pHashEntry->HashTable[*index];
1512 TRACE("entry found %d\n", *index);
1513 return TRUE;
1515 TRACE("no more entries (%d)\n", *index);
1516 return FALSE;
1519 /***********************************************************************
1520 * URLCache_DeleteCacheDirectory (Internal)
1522 * Erase a directory containing an URL cache.
1524 * RETURNS
1525 * TRUE success, FALSE failure/aborted.
1528 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1530 DWORD path_len;
1531 WCHAR path[MAX_PATH + 1];
1532 SHFILEOPSTRUCTW shfos;
1533 int ret;
1535 path_len = strlenW(lpszPath);
1536 if (path_len >= MAX_PATH)
1537 return FALSE;
1538 strcpyW(path, lpszPath);
1539 path[path_len + 1] = 0; /* double-NUL-terminate path */
1541 shfos.hwnd = NULL;
1542 shfos.wFunc = FO_DELETE;
1543 shfos.pFrom = path;
1544 shfos.pTo = NULL;
1545 shfos.fFlags = 0;
1546 shfos.fAnyOperationsAborted = FALSE;
1547 ret = SHFileOperationW(&shfos);
1548 if (ret)
1549 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1550 return !(ret || shfos.fAnyOperationsAborted);
1553 /***********************************************************************
1554 * FreeUrlCacheSpaceW (WININET.@)
1556 * Frees up some cache.
1558 * PARAMETERS
1559 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1560 * dwSize [I] How much space to free up.
1561 * dwSizeType [I] How to interpret dwSize.
1563 * RETURNS
1564 * TRUE success. FALSE failure.
1566 * IMPLEMENTATION
1567 * This implementation just retrieves the path of the cache directory, and
1568 * deletes its contents from the filesystem. The correct approach would
1569 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1571 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1573 URLCACHECONTAINER * pContainer;
1575 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1577 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1578 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1579 return FALSE;
1582 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1584 /* The URL cache has prefix L"" (unlike Cookies and History) */
1585 if (pContainer->cache_prefix[0] == 0)
1587 BOOL ret_del;
1588 DWORD ret_open;
1589 WaitForSingleObject(pContainer->hMutex, INFINITE);
1591 /* unlock, delete, recreate and lock cache */
1592 URLCacheContainer_CloseIndex(pContainer);
1593 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1594 ret_open = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1596 ReleaseMutex(pContainer->hMutex);
1597 return ret_del && (ret_open == ERROR_SUCCESS);
1600 return FALSE;
1603 /***********************************************************************
1604 * FreeUrlCacheSpaceA (WININET.@)
1606 * See FreeUrlCacheSpaceW.
1608 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1610 BOOL ret = FALSE;
1611 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1612 if (lpszCachePath == NULL || path != NULL)
1613 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1614 heap_free(path);
1615 return ret;
1618 /***********************************************************************
1619 * GetUrlCacheEntryInfoExA (WININET.@)
1622 BOOL WINAPI GetUrlCacheEntryInfoExA(
1623 LPCSTR lpszUrl,
1624 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1625 LPDWORD lpdwCacheEntryInfoBufSize,
1626 LPSTR lpszReserved,
1627 LPDWORD lpdwReserved,
1628 LPVOID lpReserved,
1629 DWORD dwFlags)
1631 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1632 debugstr_a(lpszUrl),
1633 lpCacheEntryInfo,
1634 lpdwCacheEntryInfoBufSize,
1635 lpszReserved,
1636 lpdwReserved,
1637 lpReserved,
1638 dwFlags);
1640 if ((lpszReserved != NULL) ||
1641 (lpdwReserved != NULL) ||
1642 (lpReserved != NULL))
1644 ERR("Reserved value was not 0\n");
1645 SetLastError(ERROR_INVALID_PARAMETER);
1646 return FALSE;
1648 if (dwFlags != 0)
1650 FIXME("Undocumented flag(s): %x\n", dwFlags);
1651 SetLastError(ERROR_FILE_NOT_FOUND);
1652 return FALSE;
1654 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1657 /***********************************************************************
1658 * GetUrlCacheEntryInfoA (WININET.@)
1661 BOOL WINAPI GetUrlCacheEntryInfoA(
1662 IN LPCSTR lpszUrlName,
1663 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1664 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1667 LPURLCACHE_HEADER pHeader;
1668 struct _HASH_ENTRY * pHashEntry;
1669 const CACHEFILE_ENTRY * pEntry;
1670 const URL_CACHEFILE_ENTRY * pUrlEntry;
1671 URLCACHECONTAINER * pContainer;
1672 DWORD error;
1674 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1676 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1677 if (error != ERROR_SUCCESS)
1679 SetLastError(error);
1680 return FALSE;
1683 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1684 if (error != ERROR_SUCCESS)
1686 SetLastError(error);
1687 return FALSE;
1690 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1691 return FALSE;
1693 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1695 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1696 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1697 SetLastError(ERROR_FILE_NOT_FOUND);
1698 return FALSE;
1701 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1702 if (pEntry->dwSignature != URL_SIGNATURE)
1704 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1705 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1706 SetLastError(ERROR_FILE_NOT_FOUND);
1707 return FALSE;
1710 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1711 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1712 if (pUrlEntry->dwOffsetHeaderInfo)
1713 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1715 if (lpdwCacheEntryInfoBufferSize)
1717 if (!lpCacheEntryInfo)
1718 *lpdwCacheEntryInfoBufferSize = 0;
1720 error = URLCache_CopyEntry(
1721 pContainer,
1722 pHeader,
1723 lpCacheEntryInfo,
1724 lpdwCacheEntryInfoBufferSize,
1725 pUrlEntry,
1726 FALSE /* ANSI */);
1727 if (error != ERROR_SUCCESS)
1729 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1730 SetLastError(error);
1731 return FALSE;
1733 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1736 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1738 return TRUE;
1741 /***********************************************************************
1742 * GetUrlCacheEntryInfoW (WININET.@)
1745 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1746 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1747 LPDWORD lpdwCacheEntryInfoBufferSize)
1749 LPURLCACHE_HEADER pHeader;
1750 struct _HASH_ENTRY * pHashEntry;
1751 const CACHEFILE_ENTRY * pEntry;
1752 const URL_CACHEFILE_ENTRY * pUrlEntry;
1753 URLCACHECONTAINER * pContainer;
1754 DWORD error;
1756 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1758 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1759 if (error != ERROR_SUCCESS)
1761 SetLastError(error);
1762 return FALSE;
1765 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1766 if (error != ERROR_SUCCESS)
1768 SetLastError(error);
1769 return FALSE;
1772 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1773 return FALSE;
1775 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1777 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1778 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1779 SetLastError(ERROR_FILE_NOT_FOUND);
1780 return FALSE;
1783 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1784 if (pEntry->dwSignature != URL_SIGNATURE)
1786 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1787 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1788 SetLastError(ERROR_FILE_NOT_FOUND);
1789 return FALSE;
1792 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1793 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1794 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1796 if (lpdwCacheEntryInfoBufferSize)
1798 if (!lpCacheEntryInfo)
1799 *lpdwCacheEntryInfoBufferSize = 0;
1801 error = URLCache_CopyEntry(
1802 pContainer,
1803 pHeader,
1804 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1805 lpdwCacheEntryInfoBufferSize,
1806 pUrlEntry,
1807 TRUE /* UNICODE */);
1808 if (error != ERROR_SUCCESS)
1810 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1811 SetLastError(error);
1812 return FALSE;
1814 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1817 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1819 return TRUE;
1822 /***********************************************************************
1823 * GetUrlCacheEntryInfoExW (WININET.@)
1826 BOOL WINAPI GetUrlCacheEntryInfoExW(
1827 LPCWSTR lpszUrl,
1828 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1829 LPDWORD lpdwCacheEntryInfoBufSize,
1830 LPWSTR lpszReserved,
1831 LPDWORD lpdwReserved,
1832 LPVOID lpReserved,
1833 DWORD dwFlags)
1835 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1836 debugstr_w(lpszUrl),
1837 lpCacheEntryInfo,
1838 lpdwCacheEntryInfoBufSize,
1839 lpszReserved,
1840 lpdwReserved,
1841 lpReserved,
1842 dwFlags);
1844 if ((lpszReserved != NULL) ||
1845 (lpdwReserved != NULL) ||
1846 (lpReserved != NULL))
1848 ERR("Reserved value was not 0\n");
1849 SetLastError(ERROR_INVALID_PARAMETER);
1850 return FALSE;
1852 if (dwFlags != 0)
1854 FIXME("Undocumented flag(s): %x\n", dwFlags);
1855 SetLastError(ERROR_FILE_NOT_FOUND);
1856 return FALSE;
1858 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1861 /***********************************************************************
1862 * SetUrlCacheEntryInfoA (WININET.@)
1864 BOOL WINAPI SetUrlCacheEntryInfoA(
1865 LPCSTR lpszUrlName,
1866 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1867 DWORD dwFieldControl)
1869 LPURLCACHE_HEADER pHeader;
1870 struct _HASH_ENTRY * pHashEntry;
1871 CACHEFILE_ENTRY * pEntry;
1872 URLCACHECONTAINER * pContainer;
1873 DWORD error;
1875 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1877 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1878 if (error != ERROR_SUCCESS)
1880 SetLastError(error);
1881 return FALSE;
1884 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1885 if (error != ERROR_SUCCESS)
1887 SetLastError(error);
1888 return FALSE;
1891 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1892 return FALSE;
1894 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1896 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1897 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1898 SetLastError(ERROR_FILE_NOT_FOUND);
1899 return FALSE;
1902 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1903 if (pEntry->dwSignature != URL_SIGNATURE)
1905 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1906 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1907 SetLastError(ERROR_FILE_NOT_FOUND);
1908 return FALSE;
1911 URLCache_SetEntryInfo(
1912 (URL_CACHEFILE_ENTRY *)pEntry,
1913 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1914 dwFieldControl);
1916 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1918 return TRUE;
1921 /***********************************************************************
1922 * SetUrlCacheEntryInfoW (WININET.@)
1924 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1926 LPURLCACHE_HEADER pHeader;
1927 struct _HASH_ENTRY * pHashEntry;
1928 CACHEFILE_ENTRY * pEntry;
1929 URLCACHECONTAINER * pContainer;
1930 DWORD error;
1932 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1934 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1935 if (error != ERROR_SUCCESS)
1937 SetLastError(error);
1938 return FALSE;
1941 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1942 if (error != ERROR_SUCCESS)
1944 SetLastError(error);
1945 return FALSE;
1948 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1949 return FALSE;
1951 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1953 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1954 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1955 SetLastError(ERROR_FILE_NOT_FOUND);
1956 return FALSE;
1959 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1960 if (pEntry->dwSignature != URL_SIGNATURE)
1962 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1963 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1964 SetLastError(ERROR_FILE_NOT_FOUND);
1965 return FALSE;
1968 URLCache_SetEntryInfo(
1969 (URL_CACHEFILE_ENTRY *)pEntry,
1970 lpCacheEntryInfo,
1971 dwFieldControl);
1973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1975 return TRUE;
1978 /***********************************************************************
1979 * RetrieveUrlCacheEntryFileA (WININET.@)
1982 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1983 IN LPCSTR lpszUrlName,
1984 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1985 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1986 IN DWORD dwReserved
1989 LPURLCACHE_HEADER pHeader;
1990 struct _HASH_ENTRY * pHashEntry;
1991 CACHEFILE_ENTRY * pEntry;
1992 URL_CACHEFILE_ENTRY * pUrlEntry;
1993 URLCACHECONTAINER * pContainer;
1994 DWORD error;
1996 TRACE("(%s, %p, %p, 0x%08x)\n",
1997 debugstr_a(lpszUrlName),
1998 lpCacheEntryInfo,
1999 lpdwCacheEntryInfoBufferSize,
2000 dwReserved);
2002 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2003 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2005 SetLastError(ERROR_INVALID_PARAMETER);
2006 return FALSE;
2009 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2010 if (error != ERROR_SUCCESS)
2012 SetLastError(error);
2013 return FALSE;
2016 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2017 if (error != ERROR_SUCCESS)
2019 SetLastError(error);
2020 return FALSE;
2023 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2024 return FALSE;
2026 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2028 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2029 TRACE("entry %s not found!\n", lpszUrlName);
2030 SetLastError(ERROR_FILE_NOT_FOUND);
2031 return FALSE;
2034 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2035 if (pEntry->dwSignature != URL_SIGNATURE)
2037 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2038 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2039 SetLastError(ERROR_FILE_NOT_FOUND);
2040 return FALSE;
2043 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2044 if (!pUrlEntry->dwOffsetLocalName)
2046 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2047 SetLastError(ERROR_INVALID_DATA);
2048 return FALSE;
2051 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2052 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2054 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2055 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2056 FALSE);
2057 if (error != ERROR_SUCCESS)
2059 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2060 SetLastError(error);
2061 return FALSE;
2063 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2065 pUrlEntry->dwHitRate++;
2066 pUrlEntry->dwUseCount++;
2067 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2068 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2070 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2072 return TRUE;
2075 /***********************************************************************
2076 * RetrieveUrlCacheEntryFileW (WININET.@)
2079 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2080 IN LPCWSTR lpszUrlName,
2081 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2082 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2083 IN DWORD dwReserved
2086 LPURLCACHE_HEADER pHeader;
2087 struct _HASH_ENTRY * pHashEntry;
2088 CACHEFILE_ENTRY * pEntry;
2089 URL_CACHEFILE_ENTRY * pUrlEntry;
2090 URLCACHECONTAINER * pContainer;
2091 DWORD error;
2093 TRACE("(%s, %p, %p, 0x%08x)\n",
2094 debugstr_w(lpszUrlName),
2095 lpCacheEntryInfo,
2096 lpdwCacheEntryInfoBufferSize,
2097 dwReserved);
2099 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2100 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2102 SetLastError(ERROR_INVALID_PARAMETER);
2103 return FALSE;
2106 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2107 if (error != ERROR_SUCCESS)
2109 SetLastError(error);
2110 return FALSE;
2113 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2114 if (error != ERROR_SUCCESS)
2116 SetLastError(error);
2117 return FALSE;
2120 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2121 return FALSE;
2123 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2125 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2126 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2127 SetLastError(ERROR_FILE_NOT_FOUND);
2128 return FALSE;
2131 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2132 if (pEntry->dwSignature != URL_SIGNATURE)
2134 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2135 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2136 SetLastError(ERROR_FILE_NOT_FOUND);
2137 return FALSE;
2140 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2141 if (!pUrlEntry->dwOffsetLocalName)
2143 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2144 SetLastError(ERROR_INVALID_DATA);
2145 return FALSE;
2148 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2149 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2151 error = URLCache_CopyEntry(
2152 pContainer,
2153 pHeader,
2154 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2155 lpdwCacheEntryInfoBufferSize,
2156 pUrlEntry,
2157 TRUE /* UNICODE */);
2158 if (error != ERROR_SUCCESS)
2160 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2161 SetLastError(error);
2162 return FALSE;
2164 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2166 pUrlEntry->dwHitRate++;
2167 pUrlEntry->dwUseCount++;
2168 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2169 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2171 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2173 return TRUE;
2176 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2177 struct _HASH_ENTRY *pHashEntry)
2179 CACHEFILE_ENTRY * pEntry;
2180 URL_CACHEFILE_ENTRY * pUrlEntry;
2182 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2183 if (pEntry->dwSignature != URL_SIGNATURE)
2185 FIXME("Trying to delete entry of unknown format %s\n",
2186 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2187 SetLastError(ERROR_FILE_NOT_FOUND);
2188 return FALSE;
2191 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2192 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2194 /* FIXME: implement timeout object unlocking */
2195 TRACE("Trying to delete locked entry\n");
2196 pUrlEntry->CacheEntryType |= DELETED_CACHE_ENTRY;
2197 SetLastError(ERROR_SHARING_VIOLATION);
2198 return FALSE;
2201 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2203 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2204 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2206 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2208 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2209 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2210 else
2211 pHeader->ExemptUsage.QuadPart = 0;
2213 else
2215 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2216 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2217 else
2218 pHeader->CacheUsage.QuadPart = 0;
2221 URLCache_DeleteEntry(pHeader, pEntry);
2223 URLCache_DeleteEntryFromHash(pHashEntry);
2224 return TRUE;
2227 /***********************************************************************
2228 * UnlockUrlCacheEntryFileA (WININET.@)
2231 BOOL WINAPI UnlockUrlCacheEntryFileA(
2232 IN LPCSTR lpszUrlName,
2233 IN DWORD dwReserved
2236 LPURLCACHE_HEADER pHeader;
2237 struct _HASH_ENTRY * pHashEntry;
2238 CACHEFILE_ENTRY * pEntry;
2239 URL_CACHEFILE_ENTRY * pUrlEntry;
2240 URLCACHECONTAINER * pContainer;
2241 DWORD error;
2243 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2245 if (dwReserved)
2247 ERR("dwReserved != 0\n");
2248 SetLastError(ERROR_INVALID_PARAMETER);
2249 return FALSE;
2252 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2253 if (error != ERROR_SUCCESS)
2255 SetLastError(error);
2256 return FALSE;
2259 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2260 if (error != ERROR_SUCCESS)
2262 SetLastError(error);
2263 return FALSE;
2266 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2267 return FALSE;
2269 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2271 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2272 TRACE("entry %s not found!\n", lpszUrlName);
2273 SetLastError(ERROR_FILE_NOT_FOUND);
2274 return FALSE;
2277 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2278 if (pEntry->dwSignature != URL_SIGNATURE)
2280 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2281 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2282 SetLastError(ERROR_FILE_NOT_FOUND);
2283 return FALSE;
2286 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2288 if (pUrlEntry->dwUseCount == 0)
2290 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2291 return FALSE;
2293 pUrlEntry->dwUseCount--;
2294 if (!pUrlEntry->dwUseCount)
2296 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2297 if (pUrlEntry->CacheEntryType & DELETED_CACHE_ENTRY)
2298 DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
2301 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2303 return TRUE;
2306 /***********************************************************************
2307 * UnlockUrlCacheEntryFileW (WININET.@)
2310 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2312 LPURLCACHE_HEADER pHeader;
2313 struct _HASH_ENTRY * pHashEntry;
2314 CACHEFILE_ENTRY * pEntry;
2315 URL_CACHEFILE_ENTRY * pUrlEntry;
2316 URLCACHECONTAINER * pContainer;
2317 DWORD error;
2319 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2321 if (dwReserved)
2323 ERR("dwReserved != 0\n");
2324 SetLastError(ERROR_INVALID_PARAMETER);
2325 return FALSE;
2328 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2329 if (error != ERROR_SUCCESS)
2331 SetLastError(error);
2332 return FALSE;
2335 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2336 if (error != ERROR_SUCCESS)
2338 SetLastError(error);
2339 return FALSE;
2342 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2343 return FALSE;
2345 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2347 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2348 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2349 SetLastError(ERROR_FILE_NOT_FOUND);
2350 return FALSE;
2353 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2354 if (pEntry->dwSignature != URL_SIGNATURE)
2356 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2357 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2358 SetLastError(ERROR_FILE_NOT_FOUND);
2359 return FALSE;
2362 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2364 if (pUrlEntry->dwUseCount == 0)
2366 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2367 return FALSE;
2369 pUrlEntry->dwUseCount--;
2370 if (!pUrlEntry->dwUseCount)
2371 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2373 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2375 return TRUE;
2378 /***********************************************************************
2379 * CreateUrlCacheEntryA (WININET.@)
2382 BOOL WINAPI CreateUrlCacheEntryA(
2383 IN LPCSTR lpszUrlName,
2384 IN DWORD dwExpectedFileSize,
2385 IN LPCSTR lpszFileExtension,
2386 OUT LPSTR lpszFileName,
2387 IN DWORD dwReserved
2390 WCHAR *url_name;
2391 WCHAR *file_extension = NULL;
2392 WCHAR file_name[MAX_PATH];
2393 BOOL bSuccess = FALSE;
2394 DWORD dwError = 0;
2396 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2397 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2399 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2401 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2403 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2405 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2407 bSuccess = TRUE;
2409 else
2411 dwError = GetLastError();
2414 else
2416 dwError = GetLastError();
2418 heap_free(file_extension);
2420 else
2422 dwError = GetLastError();
2424 heap_free(url_name);
2425 if (!bSuccess) SetLastError(dwError);
2427 return bSuccess;
2429 /***********************************************************************
2430 * CreateUrlCacheEntryW (WININET.@)
2433 BOOL WINAPI CreateUrlCacheEntryW(
2434 IN LPCWSTR lpszUrlName,
2435 IN DWORD dwExpectedFileSize,
2436 IN LPCWSTR lpszFileExtension,
2437 OUT LPWSTR lpszFileName,
2438 IN DWORD dwReserved
2441 URLCACHECONTAINER * pContainer;
2442 LPURLCACHE_HEADER pHeader;
2443 CHAR szFile[MAX_PATH];
2444 WCHAR szExtension[MAX_PATH];
2445 LPCWSTR lpszUrlPart;
2446 LPCWSTR lpszUrlEnd;
2447 LPCWSTR lpszFileNameExtension;
2448 LPWSTR lpszFileNameNoPath;
2449 int i;
2450 int countnoextension;
2451 BYTE CacheDir;
2452 LONG lBufferSize;
2453 BOOL bFound = FALSE;
2454 int count;
2455 DWORD error;
2456 HANDLE hFile;
2457 FILETIME ft;
2459 static const WCHAR szWWW[] = {'w','w','w',0};
2460 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2462 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2463 debugstr_w(lpszUrlName),
2464 dwExpectedFileSize,
2465 debugstr_w(lpszFileExtension),
2466 lpszFileName,
2467 dwReserved);
2469 if (dwReserved)
2470 FIXME("dwReserved 0x%08x\n", dwReserved);
2472 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2474 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2475 lpszUrlEnd--;
2477 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2478 if (!lpszUrlPart)
2479 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2480 if (lpszUrlPart)
2481 lpszUrlEnd = lpszUrlPart;
2483 for (lpszUrlPart = lpszUrlEnd;
2484 (lpszUrlPart >= lpszUrlName);
2485 lpszUrlPart--)
2487 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2489 bFound = TRUE;
2490 lpszUrlPart++;
2491 break;
2494 if (!lstrcmpW(lpszUrlPart, szWWW))
2496 lpszUrlPart += lstrlenW(szWWW);
2499 count = lpszUrlEnd - lpszUrlPart;
2501 if (bFound && (count < MAX_PATH))
2503 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2504 if (!len)
2505 return FALSE;
2506 szFile[len] = '\0';
2507 while(len && szFile[--len] == '/') szFile[len] = '\0';
2509 /* FIXME: get rid of illegal characters like \, / and : */
2511 else
2513 FIXME("need to generate a random filename\n");
2516 TRACE("File name: %s\n", debugstr_a(szFile));
2518 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2519 if (error != ERROR_SUCCESS)
2521 SetLastError(error);
2522 return FALSE;
2525 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2526 if (error != ERROR_SUCCESS)
2528 SetLastError(error);
2529 return FALSE;
2532 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2533 return FALSE;
2535 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2537 lBufferSize = MAX_PATH * sizeof(WCHAR);
2538 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2540 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2541 debugstr_a(szFile), lBufferSize);
2542 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2543 return FALSE;
2546 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2548 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2549 lpszFileNameNoPath >= lpszFileName;
2550 --lpszFileNameNoPath)
2552 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2553 break;
2556 countnoextension = lstrlenW(lpszFileNameNoPath);
2557 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2558 if (lpszFileNameExtension)
2559 countnoextension -= lstrlenW(lpszFileNameExtension);
2560 *szExtension = '\0';
2562 if (lpszFileExtension)
2564 szExtension[0] = '.';
2565 lstrcpyW(szExtension+1, lpszFileExtension);
2568 for (i = 0; i < 255; i++)
2570 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2571 WCHAR *p;
2573 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2574 for (p = lpszFileNameNoPath + 1; *p; p++)
2576 switch (*p)
2578 case '<': case '>':
2579 case ':': case '"':
2580 case '/': case '\\':
2581 case '|': case '?':
2582 case '*':
2583 *p = '_'; break;
2584 default: break;
2587 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2589 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2590 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2591 if (hFile != INVALID_HANDLE_VALUE)
2593 CloseHandle(hFile);
2594 return TRUE;
2598 GetSystemTimeAsFileTime(&ft);
2599 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2601 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2602 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2603 if (hFile != INVALID_HANDLE_VALUE)
2605 CloseHandle(hFile);
2606 return TRUE;
2609 WARN("Could not find a unique filename\n");
2610 return FALSE;
2614 /***********************************************************************
2615 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2617 * The bug we are compensating for is that some drongo at Microsoft
2618 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2619 * As a consequence, CommitUrlCacheEntryA has been effectively
2620 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2621 * is still defined as LPCWSTR. The result (other than madness) is
2622 * that we always need to store lpHeaderInfo in CP_ACP rather than
2623 * in UTF16, and we need to avoid converting lpHeaderInfo in
2624 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2625 * result will lose data for arbitrary binary data.
2628 static BOOL CommitUrlCacheEntryInternal(
2629 IN LPCWSTR lpszUrlName,
2630 IN LPCWSTR lpszLocalFileName,
2631 IN FILETIME ExpireTime,
2632 IN FILETIME LastModifiedTime,
2633 IN DWORD CacheEntryType,
2634 IN LPBYTE lpHeaderInfo,
2635 IN DWORD dwHeaderSize,
2636 IN LPCWSTR lpszFileExtension,
2637 IN LPCWSTR lpszOriginalUrl
2640 URLCACHECONTAINER * pContainer;
2641 LPURLCACHE_HEADER pHeader;
2642 struct _HASH_ENTRY * pHashEntry;
2643 CACHEFILE_ENTRY * pEntry;
2644 URL_CACHEFILE_ENTRY * pUrlEntry;
2645 DWORD url_entry_offset;
2646 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2647 DWORD dwOffsetLocalFileName = 0;
2648 DWORD dwOffsetHeader = 0;
2649 DWORD dwOffsetFileExtension = 0;
2650 LARGE_INTEGER file_size;
2651 BYTE cDirectory = 0;
2652 char achFile[MAX_PATH];
2653 LPSTR lpszUrlNameA = NULL;
2654 LPSTR lpszFileExtensionA = NULL;
2655 char *pchLocalFileName = 0;
2656 DWORD error;
2658 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2659 debugstr_w(lpszUrlName),
2660 debugstr_w(lpszLocalFileName),
2661 CacheEntryType,
2662 lpHeaderInfo,
2663 dwHeaderSize,
2664 debugstr_w(lpszFileExtension),
2665 debugstr_w(lpszOriginalUrl));
2667 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2669 SetLastError(ERROR_INVALID_PARAMETER);
2670 return FALSE;
2672 if (lpszOriginalUrl)
2673 WARN(": lpszOriginalUrl ignored\n");
2675 file_size.QuadPart = 0;
2676 if (lpszLocalFileName)
2678 HANDLE hFile;
2680 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2681 if (hFile == INVALID_HANDLE_VALUE)
2683 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2684 return FALSE;
2687 /* Get file size */
2688 if (!GetFileSizeEx(hFile, &file_size))
2690 ERR("couldn't get file size (error is %d)\n", GetLastError());
2691 CloseHandle(hFile);
2692 return FALSE;
2695 CloseHandle(hFile);
2698 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2699 if (error != ERROR_SUCCESS)
2701 SetLastError(error);
2702 return FALSE;
2705 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2706 if (error != ERROR_SUCCESS)
2708 SetLastError(error);
2709 return FALSE;
2712 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2713 return FALSE;
2715 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2716 if (!lpszUrlNameA)
2718 error = GetLastError();
2719 goto cleanup;
2722 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2724 error = GetLastError();
2725 goto cleanup;
2728 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2730 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2732 /* FIXME: implement timeout object unlocking */
2733 FIXME("Trying to overwrite locked entry\n");
2734 SetLastError(ERROR_SHARING_VIOLATION);
2735 goto cleanup;
2738 FIXME("entry already in cache - don't know what to do!\n");
2740 * SetLastError(ERROR_FILE_NOT_FOUND);
2741 * return FALSE;
2743 goto cleanup;
2746 if (lpszLocalFileName)
2748 BOOL bFound = FALSE;
2750 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2752 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2753 error = ERROR_INVALID_PARAMETER;
2754 goto cleanup;
2757 /* skip container path prefix */
2758 lpszLocalFileName += lstrlenW(pContainer->path);
2760 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2761 pchLocalFileName = achFile;
2763 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2765 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2767 bFound = TRUE;
2768 break;
2772 if (!bFound)
2774 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2775 error = ERROR_INVALID_PARAMETER;
2776 goto cleanup;
2779 lpszLocalFileName += DIR_LENGTH + 1;
2780 pchLocalFileName += DIR_LENGTH + 1;
2783 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2784 if (lpszLocalFileName)
2786 dwOffsetLocalFileName = dwBytesNeeded;
2787 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2789 if (lpHeaderInfo)
2791 dwOffsetHeader = dwBytesNeeded;
2792 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2794 if (lpszFileExtensionA)
2796 dwOffsetFileExtension = dwBytesNeeded;
2797 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2800 /* round up to next block */
2801 if (dwBytesNeeded % BLOCKSIZE)
2803 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2804 dwBytesNeeded += BLOCKSIZE;
2807 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2808 while (error == ERROR_HANDLE_DISK_FULL)
2810 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2811 if (error == ERROR_SUCCESS)
2812 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2814 if (error != ERROR_SUCCESS)
2815 goto cleanup;
2817 /* FindFirstFreeEntry fills in blocks used */
2818 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2819 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
2820 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2821 pUrlEntry->CacheDir = cDirectory;
2822 pUrlEntry->CacheEntryType = CacheEntryType;
2823 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2824 if (CacheEntryType & STICKY_CACHE_ENTRY)
2826 /* Sticky entries have a default exempt time of one day */
2827 pUrlEntry->dwExemptDelta = 86400;
2829 else
2830 pUrlEntry->dwExemptDelta = 0;
2831 pUrlEntry->dwHitRate = 0;
2832 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2833 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2834 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2835 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2836 pUrlEntry->size.QuadPart = file_size.QuadPart;
2837 pUrlEntry->dwUseCount = 0;
2838 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2839 pUrlEntry->LastModifiedTime = LastModifiedTime;
2840 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2841 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2842 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2843 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2845 /*** Unknowns ***/
2846 pUrlEntry->dwUnknown1 = 0;
2847 pUrlEntry->dwUnknown2 = 0;
2848 pUrlEntry->dwUnknown3 = 0x60;
2849 pUrlEntry->Unknown4 = 0;
2850 pUrlEntry->wUnknown5 = 0x1010;
2851 pUrlEntry->dwUnknown7 = 0;
2852 pUrlEntry->dwUnknown8 = 0;
2855 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2856 if (dwOffsetLocalFileName)
2857 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
2858 if (dwOffsetHeader)
2859 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2860 if (dwOffsetFileExtension)
2861 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2863 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
2864 while (error == ERROR_HANDLE_DISK_FULL)
2866 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2867 if (error == ERROR_SUCCESS)
2869 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
2870 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2871 url_entry_offset, HASHTABLE_URL);
2874 if (error != ERROR_SUCCESS)
2875 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2876 else
2878 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2879 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2880 if (CacheEntryType & STICKY_CACHE_ENTRY)
2881 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2882 else
2883 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2884 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2885 pHeader->CacheLimit.QuadPart)
2886 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2889 cleanup:
2890 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2891 heap_free(lpszUrlNameA);
2892 heap_free(lpszFileExtensionA);
2894 if (error == ERROR_SUCCESS)
2895 return TRUE;
2896 else
2898 SetLastError(error);
2899 return FALSE;
2903 /***********************************************************************
2904 * CommitUrlCacheEntryA (WININET.@)
2907 BOOL WINAPI CommitUrlCacheEntryA(
2908 IN LPCSTR lpszUrlName,
2909 IN LPCSTR lpszLocalFileName,
2910 IN FILETIME ExpireTime,
2911 IN FILETIME LastModifiedTime,
2912 IN DWORD CacheEntryType,
2913 IN LPBYTE lpHeaderInfo,
2914 IN DWORD dwHeaderSize,
2915 IN LPCSTR lpszFileExtension,
2916 IN LPCSTR lpszOriginalUrl
2919 WCHAR *url_name = NULL;
2920 WCHAR *local_file_name = NULL;
2921 WCHAR *original_url = NULL;
2922 WCHAR *file_extension = NULL;
2923 BOOL bSuccess = FALSE;
2925 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2926 debugstr_a(lpszUrlName),
2927 debugstr_a(lpszLocalFileName),
2928 CacheEntryType,
2929 lpHeaderInfo,
2930 dwHeaderSize,
2931 debugstr_a(lpszFileExtension),
2932 debugstr_a(lpszOriginalUrl));
2934 url_name = heap_strdupAtoW(lpszUrlName);
2935 if (!url_name)
2936 goto cleanup;
2938 if (lpszLocalFileName)
2940 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2941 if (!local_file_name)
2942 goto cleanup;
2944 if (lpszFileExtension)
2946 file_extension = heap_strdupAtoW(lpszFileExtension);
2947 if (!file_extension)
2948 goto cleanup;
2950 if (lpszOriginalUrl)
2952 original_url = heap_strdupAtoW(lpszOriginalUrl);
2953 if (!original_url)
2954 goto cleanup;
2957 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2958 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2959 file_extension, original_url);
2961 cleanup:
2962 heap_free(original_url);
2963 heap_free(file_extension);
2964 heap_free(local_file_name);
2965 heap_free(url_name);
2966 return bSuccess;
2969 /***********************************************************************
2970 * CommitUrlCacheEntryW (WININET.@)
2973 BOOL WINAPI CommitUrlCacheEntryW(
2974 IN LPCWSTR lpszUrlName,
2975 IN LPCWSTR lpszLocalFileName,
2976 IN FILETIME ExpireTime,
2977 IN FILETIME LastModifiedTime,
2978 IN DWORD CacheEntryType,
2979 IN LPWSTR lpHeaderInfo,
2980 IN DWORD dwHeaderSize,
2981 IN LPCWSTR lpszFileExtension,
2982 IN LPCWSTR lpszOriginalUrl
2985 DWORD dwError = 0;
2986 BOOL bSuccess = FALSE;
2987 DWORD len = 0;
2988 CHAR *header_info = NULL;
2990 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2991 debugstr_w(lpszUrlName),
2992 debugstr_w(lpszLocalFileName),
2993 CacheEntryType,
2994 lpHeaderInfo,
2995 dwHeaderSize,
2996 debugstr_w(lpszFileExtension),
2997 debugstr_w(lpszOriginalUrl));
2999 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3001 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3002 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3004 bSuccess = TRUE;
3006 else
3008 dwError = GetLastError();
3010 if (header_info)
3012 heap_free(header_info);
3013 if (!bSuccess)
3014 SetLastError(dwError);
3017 return bSuccess;
3020 /***********************************************************************
3021 * ReadUrlCacheEntryStream (WININET.@)
3024 BOOL WINAPI ReadUrlCacheEntryStream(
3025 IN HANDLE hUrlCacheStream,
3026 IN DWORD dwLocation,
3027 IN OUT LPVOID lpBuffer,
3028 IN OUT LPDWORD lpdwLen,
3029 IN DWORD dwReserved
3032 /* Get handle to file from 'stream' */
3033 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3035 if (dwReserved != 0)
3037 ERR("dwReserved != 0\n");
3038 SetLastError(ERROR_INVALID_PARAMETER);
3039 return FALSE;
3042 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3044 SetLastError(ERROR_INVALID_HANDLE);
3045 return FALSE;
3048 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3049 return FALSE;
3050 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3053 /***********************************************************************
3054 * RetrieveUrlCacheEntryStreamA (WININET.@)
3057 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3058 IN LPCSTR lpszUrlName,
3059 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3060 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3061 IN BOOL fRandomRead,
3062 IN DWORD dwReserved
3065 /* NOTE: this is not the same as the way that the native
3066 * version allocates 'stream' handles. I did it this way
3067 * as it is much easier and no applications should depend
3068 * on this behaviour. (Native version appears to allocate
3069 * indices into a table)
3071 STREAM_HANDLE * pStream;
3072 HANDLE hFile;
3074 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3075 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3077 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3078 lpCacheEntryInfo,
3079 lpdwCacheEntryInfoBufferSize,
3080 dwReserved))
3082 return NULL;
3085 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3086 GENERIC_READ,
3087 FILE_SHARE_READ,
3088 NULL,
3089 OPEN_EXISTING,
3090 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3091 NULL);
3092 if (hFile == INVALID_HANDLE_VALUE)
3093 return FALSE;
3095 /* allocate handle storage space */
3096 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3097 if (!pStream)
3099 CloseHandle(hFile);
3100 SetLastError(ERROR_OUTOFMEMORY);
3101 return FALSE;
3104 pStream->hFile = hFile;
3105 strcpy(pStream->lpszUrl, lpszUrlName);
3106 return pStream;
3109 /***********************************************************************
3110 * RetrieveUrlCacheEntryStreamW (WININET.@)
3113 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3114 IN LPCWSTR lpszUrlName,
3115 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3116 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3117 IN BOOL fRandomRead,
3118 IN DWORD dwReserved
3121 DWORD size;
3122 int url_len;
3123 /* NOTE: this is not the same as the way that the native
3124 * version allocates 'stream' handles. I did it this way
3125 * as it is much easier and no applications should depend
3126 * on this behaviour. (Native version appears to allocate
3127 * indices into a table)
3129 STREAM_HANDLE * pStream;
3130 HANDLE hFile;
3132 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3133 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3135 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3136 lpCacheEntryInfo,
3137 lpdwCacheEntryInfoBufferSize,
3138 dwReserved))
3140 return NULL;
3143 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3144 GENERIC_READ,
3145 FILE_SHARE_READ,
3146 NULL,
3147 OPEN_EXISTING,
3148 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3149 NULL);
3150 if (hFile == INVALID_HANDLE_VALUE)
3151 return FALSE;
3153 /* allocate handle storage space */
3154 size = sizeof(STREAM_HANDLE);
3155 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3156 size += url_len;
3157 pStream = heap_alloc(size);
3158 if (!pStream)
3160 CloseHandle(hFile);
3161 SetLastError(ERROR_OUTOFMEMORY);
3162 return FALSE;
3165 pStream->hFile = hFile;
3166 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3167 return pStream;
3170 /***********************************************************************
3171 * UnlockUrlCacheEntryStream (WININET.@)
3174 BOOL WINAPI UnlockUrlCacheEntryStream(
3175 IN HANDLE hUrlCacheStream,
3176 IN DWORD dwReserved
3179 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3181 if (dwReserved != 0)
3183 ERR("dwReserved != 0\n");
3184 SetLastError(ERROR_INVALID_PARAMETER);
3185 return FALSE;
3188 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3190 SetLastError(ERROR_INVALID_HANDLE);
3191 return FALSE;
3194 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3195 return FALSE;
3197 CloseHandle(pStream->hFile);
3198 heap_free(pStream);
3199 return TRUE;
3203 /***********************************************************************
3204 * DeleteUrlCacheEntryA (WININET.@)
3207 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3209 URLCACHECONTAINER * pContainer;
3210 LPURLCACHE_HEADER pHeader;
3211 struct _HASH_ENTRY * pHashEntry;
3212 DWORD error;
3213 BOOL ret;
3215 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3217 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3218 if (error != ERROR_SUCCESS)
3220 SetLastError(error);
3221 return FALSE;
3224 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3225 if (error != ERROR_SUCCESS)
3227 SetLastError(error);
3228 return FALSE;
3231 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3232 return FALSE;
3234 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3236 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3237 TRACE("entry %s not found!\n", lpszUrlName);
3238 SetLastError(ERROR_FILE_NOT_FOUND);
3239 return FALSE;
3242 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3244 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3246 return ret;
3249 /***********************************************************************
3250 * DeleteUrlCacheEntryW (WININET.@)
3253 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3255 URLCACHECONTAINER * pContainer;
3256 LPURLCACHE_HEADER pHeader;
3257 struct _HASH_ENTRY * pHashEntry;
3258 LPSTR urlA;
3259 DWORD error;
3260 BOOL ret;
3262 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3264 urlA = heap_strdupWtoA(lpszUrlName);
3265 if (!urlA)
3267 SetLastError(ERROR_OUTOFMEMORY);
3268 return FALSE;
3271 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3272 if (error != ERROR_SUCCESS)
3274 heap_free(urlA);
3275 SetLastError(error);
3276 return FALSE;
3279 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3280 if (error != ERROR_SUCCESS)
3282 heap_free(urlA);
3283 SetLastError(error);
3284 return FALSE;
3287 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3289 heap_free(urlA);
3290 return FALSE;
3293 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3295 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3296 TRACE("entry %s not found!\n", debugstr_a(urlA));
3297 heap_free(urlA);
3298 SetLastError(ERROR_FILE_NOT_FOUND);
3299 return FALSE;
3302 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3304 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3305 heap_free(urlA);
3306 return ret;
3309 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3311 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3312 return TRUE;
3315 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3317 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3318 return TRUE;
3321 /***********************************************************************
3322 * CreateCacheContainerA (WININET.@)
3324 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3325 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3327 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3328 d1, d2, d3, d4, d5, d6, d7, d8);
3329 return TRUE;
3332 /***********************************************************************
3333 * CreateCacheContainerW (WININET.@)
3335 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3336 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3338 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3339 d1, d2, d3, d4, d5, d6, d7, d8);
3340 return TRUE;
3343 /***********************************************************************
3344 * FindFirstUrlCacheContainerA (WININET.@)
3346 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3348 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3349 return NULL;
3352 /***********************************************************************
3353 * FindFirstUrlCacheContainerW (WININET.@)
3355 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3357 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3358 return NULL;
3361 /***********************************************************************
3362 * FindNextUrlCacheContainerA (WININET.@)
3364 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3366 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3367 return FALSE;
3370 /***********************************************************************
3371 * FindNextUrlCacheContainerW (WININET.@)
3373 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3375 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3376 return FALSE;
3379 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3380 LPCSTR lpszUrlSearchPattern,
3381 DWORD dwFlags,
3382 DWORD dwFilter,
3383 GROUPID GroupId,
3384 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3385 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3386 LPVOID lpReserved,
3387 LPDWORD pcbReserved2,
3388 LPVOID lpReserved3
3391 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3392 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3393 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3394 SetLastError(ERROR_FILE_NOT_FOUND);
3395 return NULL;
3398 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3399 LPCWSTR lpszUrlSearchPattern,
3400 DWORD dwFlags,
3401 DWORD dwFilter,
3402 GROUPID GroupId,
3403 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3404 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3405 LPVOID lpReserved,
3406 LPDWORD pcbReserved2,
3407 LPVOID lpReserved3
3410 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3411 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3412 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3413 SetLastError(ERROR_FILE_NOT_FOUND);
3414 return NULL;
3417 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3419 typedef struct URLCacheFindEntryHandle
3421 DWORD dwMagic;
3422 LPWSTR lpszUrlSearchPattern;
3423 DWORD dwContainerIndex;
3424 DWORD dwHashTableIndex;
3425 DWORD dwHashEntryIndex;
3426 } URLCacheFindEntryHandle;
3428 /***********************************************************************
3429 * FindFirstUrlCacheEntryA (WININET.@)
3432 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3433 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3435 URLCacheFindEntryHandle *pEntryHandle;
3437 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3439 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3440 if (!pEntryHandle)
3441 return NULL;
3443 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3444 if (lpszUrlSearchPattern)
3446 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3447 if (!pEntryHandle->lpszUrlSearchPattern)
3449 heap_free(pEntryHandle);
3450 return NULL;
3453 else
3454 pEntryHandle->lpszUrlSearchPattern = NULL;
3455 pEntryHandle->dwContainerIndex = 0;
3456 pEntryHandle->dwHashTableIndex = 0;
3457 pEntryHandle->dwHashEntryIndex = 0;
3459 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3461 heap_free(pEntryHandle);
3462 return NULL;
3464 return pEntryHandle;
3467 /***********************************************************************
3468 * FindFirstUrlCacheEntryW (WININET.@)
3471 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3472 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3474 URLCacheFindEntryHandle *pEntryHandle;
3476 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3478 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3479 if (!pEntryHandle)
3480 return NULL;
3482 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3483 if (lpszUrlSearchPattern)
3485 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3486 if (!pEntryHandle->lpszUrlSearchPattern)
3488 heap_free(pEntryHandle);
3489 return NULL;
3492 else
3493 pEntryHandle->lpszUrlSearchPattern = NULL;
3494 pEntryHandle->dwContainerIndex = 0;
3495 pEntryHandle->dwHashTableIndex = 0;
3496 pEntryHandle->dwHashEntryIndex = 0;
3498 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3500 heap_free(pEntryHandle);
3501 return NULL;
3503 return pEntryHandle;
3506 static BOOL FindNextUrlCacheEntryInternal(
3507 HANDLE hEnumHandle,
3508 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3509 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3510 BOOL unicode)
3512 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3513 URLCACHECONTAINER * pContainer;
3515 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3517 SetLastError(ERROR_INVALID_HANDLE);
3518 return FALSE;
3521 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3522 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3524 LPURLCACHE_HEADER pHeader;
3525 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3526 DWORD error;
3528 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3529 if (error != ERROR_SUCCESS)
3531 SetLastError(error);
3532 return FALSE;
3535 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3536 return FALSE;
3538 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3539 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3541 const struct _HASH_ENTRY *pHashEntry = NULL;
3542 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3543 pEntryHandle->dwHashEntryIndex++)
3545 const URL_CACHEFILE_ENTRY *pUrlEntry;
3546 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3548 if (pEntry->dwSignature != URL_SIGNATURE)
3549 continue;
3551 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3552 TRACE("Found URL: %s\n",
3553 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3554 TRACE("Header info: %s\n",
3555 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3557 error = URLCache_CopyEntry(
3558 pContainer,
3559 pHeader,
3560 lpNextCacheEntryInfo,
3561 lpdwNextCacheEntryInfoBufferSize,
3562 pUrlEntry,
3563 unicode);
3564 if (error != ERROR_SUCCESS)
3566 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3567 SetLastError(error);
3568 return FALSE;
3570 TRACE("Local File Name: %s\n",
3571 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3573 /* increment the current index so that next time the function
3574 * is called the next entry is returned */
3575 pEntryHandle->dwHashEntryIndex++;
3576 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3577 return TRUE;
3581 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3584 SetLastError(ERROR_NO_MORE_ITEMS);
3585 return FALSE;
3588 /***********************************************************************
3589 * FindNextUrlCacheEntryA (WININET.@)
3591 BOOL WINAPI FindNextUrlCacheEntryA(
3592 HANDLE hEnumHandle,
3593 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3594 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3596 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3598 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3599 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3602 /***********************************************************************
3603 * FindNextUrlCacheEntryW (WININET.@)
3605 BOOL WINAPI FindNextUrlCacheEntryW(
3606 HANDLE hEnumHandle,
3607 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3608 LPDWORD lpdwNextCacheEntryInfoBufferSize
3611 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3613 return FindNextUrlCacheEntryInternal(hEnumHandle,
3614 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3615 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3618 /***********************************************************************
3619 * FindCloseUrlCache (WININET.@)
3621 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3623 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3625 TRACE("(%p)\n", hEnumHandle);
3627 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3629 SetLastError(ERROR_INVALID_HANDLE);
3630 return FALSE;
3633 pEntryHandle->dwMagic = 0;
3634 heap_free(pEntryHandle->lpszUrlSearchPattern);
3635 heap_free(pEntryHandle);
3636 return TRUE;
3639 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3640 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3642 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3643 dwSearchCondition, lpGroupId, lpReserved);
3644 return NULL;
3647 BOOL WINAPI FindNextUrlCacheEntryExA(
3648 HANDLE hEnumHandle,
3649 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3650 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3651 LPVOID lpReserved,
3652 LPDWORD pcbReserved2,
3653 LPVOID lpReserved3
3656 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3657 lpReserved, pcbReserved2, lpReserved3);
3658 return FALSE;
3661 BOOL WINAPI FindNextUrlCacheEntryExW(
3662 HANDLE hEnumHandle,
3663 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3664 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3665 LPVOID lpReserved,
3666 LPDWORD pcbReserved2,
3667 LPVOID lpReserved3
3670 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3671 lpReserved, pcbReserved2, lpReserved3);
3672 return FALSE;
3675 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3677 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3678 return FALSE;
3681 /***********************************************************************
3682 * CreateUrlCacheGroup (WININET.@)
3685 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3687 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3688 return FALSE;
3691 /***********************************************************************
3692 * DeleteUrlCacheGroup (WININET.@)
3695 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3697 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3698 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3699 return FALSE;
3702 /***********************************************************************
3703 * SetUrlCacheEntryGroupA (WININET.@)
3706 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3707 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3708 LPVOID lpReserved)
3710 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3711 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3712 pbGroupAttributes, cbGroupAttributes, lpReserved);
3713 SetLastError(ERROR_FILE_NOT_FOUND);
3714 return FALSE;
3717 /***********************************************************************
3718 * SetUrlCacheEntryGroupW (WININET.@)
3721 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3722 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3723 LPVOID lpReserved)
3725 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3726 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3727 pbGroupAttributes, cbGroupAttributes, lpReserved);
3728 SetLastError(ERROR_FILE_NOT_FOUND);
3729 return FALSE;
3732 /***********************************************************************
3733 * GetUrlCacheConfigInfoW (WININET.@)
3735 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3737 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3738 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3739 return FALSE;
3742 /***********************************************************************
3743 * GetUrlCacheConfigInfoA (WININET.@)
3745 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3747 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3748 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3749 return FALSE;
3752 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3753 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3754 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3756 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3757 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3758 lpdwGroupInfo, lpReserved);
3759 return FALSE;
3762 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3763 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3764 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3766 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3767 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3768 lpdwGroupInfo, lpReserved);
3769 return FALSE;
3772 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3773 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3775 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3776 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3777 return TRUE;
3780 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3781 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3783 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3784 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3785 return TRUE;
3788 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3790 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3791 return TRUE;
3794 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3796 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3797 return TRUE;
3800 /***********************************************************************
3801 * DeleteIE3Cache (WININET.@)
3803 * Deletes the files used by the IE3 URL caching system.
3805 * PARAMS
3806 * hWnd [I] A dummy window.
3807 * hInst [I] Instance of process calling the function.
3808 * lpszCmdLine [I] Options used by function.
3809 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3811 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3813 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3814 return 0;
3817 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3818 FILETIME *pftLastModified)
3820 BOOL ret;
3821 FILETIME now, expired;
3823 *pftLastModified = pUrlEntry->LastModifiedTime;
3824 GetSystemTimeAsFileTime(&now);
3825 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3826 pUrlEntry->wExpiredTime, &expired);
3827 /* If the expired time is 0, it's interpreted as not expired */
3828 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3829 ret = FALSE;
3830 else
3831 ret = CompareFileTime(&expired, &now) < 0;
3832 return ret;
3835 /***********************************************************************
3836 * IsUrlCacheEntryExpiredA (WININET.@)
3838 * PARAMS
3839 * url [I] Url
3840 * dwFlags [I] Unknown
3841 * pftLastModified [O] Last modified time
3843 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3845 LPURLCACHE_HEADER pHeader;
3846 struct _HASH_ENTRY * pHashEntry;
3847 const CACHEFILE_ENTRY * pEntry;
3848 const URL_CACHEFILE_ENTRY * pUrlEntry;
3849 URLCACHECONTAINER * pContainer;
3850 BOOL expired;
3852 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3854 if (!url || !pftLastModified)
3855 return TRUE;
3856 if (dwFlags)
3857 FIXME("unknown flags 0x%08x\n", dwFlags);
3859 /* Any error implies that the URL is expired, i.e. not in the cache */
3860 if (URLCacheContainers_FindContainerA(url, &pContainer))
3862 memset(pftLastModified, 0, sizeof(*pftLastModified));
3863 return TRUE;
3866 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3868 memset(pftLastModified, 0, sizeof(*pftLastModified));
3869 return TRUE;
3872 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3874 memset(pftLastModified, 0, sizeof(*pftLastModified));
3875 return TRUE;
3878 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3880 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3881 memset(pftLastModified, 0, sizeof(*pftLastModified));
3882 TRACE("entry %s not found!\n", url);
3883 return TRUE;
3886 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3887 if (pEntry->dwSignature != URL_SIGNATURE)
3889 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3890 memset(pftLastModified, 0, sizeof(*pftLastModified));
3891 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3892 return TRUE;
3895 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3896 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3898 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3900 return expired;
3903 /***********************************************************************
3904 * IsUrlCacheEntryExpiredW (WININET.@)
3906 * PARAMS
3907 * url [I] Url
3908 * dwFlags [I] Unknown
3909 * pftLastModified [O] Last modified time
3911 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3913 LPURLCACHE_HEADER pHeader;
3914 struct _HASH_ENTRY * pHashEntry;
3915 const CACHEFILE_ENTRY * pEntry;
3916 const URL_CACHEFILE_ENTRY * pUrlEntry;
3917 URLCACHECONTAINER * pContainer;
3918 BOOL expired;
3920 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3922 if (!url || !pftLastModified)
3923 return TRUE;
3924 if (dwFlags)
3925 FIXME("unknown flags 0x%08x\n", dwFlags);
3927 /* Any error implies that the URL is expired, i.e. not in the cache */
3928 if (URLCacheContainers_FindContainerW(url, &pContainer))
3930 memset(pftLastModified, 0, sizeof(*pftLastModified));
3931 return TRUE;
3934 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3936 memset(pftLastModified, 0, sizeof(*pftLastModified));
3937 return TRUE;
3940 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3942 memset(pftLastModified, 0, sizeof(*pftLastModified));
3943 return TRUE;
3946 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3948 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3949 memset(pftLastModified, 0, sizeof(*pftLastModified));
3950 TRACE("entry %s not found!\n", debugstr_w(url));
3951 return TRUE;
3954 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3956 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3957 memset(pftLastModified, 0, sizeof(*pftLastModified));
3958 TRACE("entry %s not found!\n", debugstr_w(url));
3959 return TRUE;
3962 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3963 if (pEntry->dwSignature != URL_SIGNATURE)
3965 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3966 memset(pftLastModified, 0, sizeof(*pftLastModified));
3967 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3968 return TRUE;
3971 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3972 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3974 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3976 return expired;
3979 /***********************************************************************
3980 * GetDiskInfoA (WININET.@)
3982 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3984 BOOL ret;
3985 ULARGE_INTEGER bytes_free, bytes_total;
3987 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3989 if (!path)
3991 SetLastError(ERROR_INVALID_PARAMETER);
3992 return FALSE;
3995 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3997 if (cluster_size) *cluster_size = 1;
3998 if (free) *free = bytes_free.QuadPart;
3999 if (total) *total = bytes_total.QuadPart;
4001 return ret;
4004 /***********************************************************************
4005 * RegisterUrlCacheNotification (WININET.@)
4007 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4009 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4010 return 0;
4013 /***********************************************************************
4014 * IncrementUrlCacheHeaderData (WININET.@)
4016 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4018 FIXME("(%u, %p)\n", index, data);
4019 return FALSE;
4022 /***********************************************************************
4023 * RunOnceUrlCache (WININET.@)
4026 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4028 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4029 return 0;