wininet: Implement urlcache index growing.
[wine/multimedia.git] / dlls / wininet / urlcache.c
blob8f433ba7dccdde531f9f1c37ae3c4f07d998005b
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 4
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 CloseHandle(hMapping);
425 else
427 dwError = GetLastError();
431 if (dwError)
433 CloseHandle(hFile);
434 DeleteFileW(wszFilePath);
435 ReleaseMutex(pContainer->hMutex);
436 return dwError;
441 pContainer->file_size = new_file_size;
442 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, new_file_size);
443 URLCache_PathToObjectName(wszFilePath, '_');
444 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
445 if (!pContainer->hMapping)
446 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
447 CloseHandle(hFile);
448 if (!pContainer->hMapping)
450 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
451 ReleaseMutex(pContainer->hMutex);
452 return GetLastError();
455 ReleaseMutex(pContainer->hMutex);
457 return ERROR_SUCCESS;
460 /***********************************************************************
461 * URLCacheContainer_CloseIndex (Internal)
463 * Closes the index
465 * RETURNS
466 * nothing
469 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
471 CloseHandle(pContainer->hMapping);
472 pContainer->hMapping = NULL;
475 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
477 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
478 int cache_prefix_len = strlenW(cache_prefix);
480 if (!pContainer)
482 return FALSE;
485 pContainer->hMapping = NULL;
486 pContainer->file_size = 0;
488 pContainer->path = heap_strdupW(path);
489 if (!pContainer->path)
491 heap_free(pContainer);
492 return FALSE;
495 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
496 if (!pContainer->cache_prefix)
498 heap_free(pContainer->path);
499 heap_free(pContainer);
500 return FALSE;
503 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
505 CharLowerW(mutex_name);
506 URLCache_PathToObjectName(mutex_name, '!');
508 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
510 ERR("couldn't create mutex (error is %d)\n", GetLastError());
511 heap_free(pContainer->path);
512 heap_free(pContainer);
513 return FALSE;
516 list_add_head(&UrlContainers, &pContainer->entry);
518 return TRUE;
521 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
523 list_remove(&pContainer->entry);
525 URLCacheContainer_CloseIndex(pContainer);
526 CloseHandle(pContainer->hMutex);
527 heap_free(pContainer->path);
528 heap_free(pContainer->cache_prefix);
529 heap_free(pContainer);
532 void URLCacheContainers_CreateDefaults(void)
534 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
535 static const WCHAR UrlPrefix[] = {0};
536 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
537 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
538 static const WCHAR CookieSuffix[] = {0};
539 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
540 static const struct
542 int nFolder; /* CSIDL_* constant */
543 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
544 const WCHAR * cache_prefix; /* prefix used to reference the container */
545 } DefaultContainerData[] =
547 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
548 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
549 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
551 DWORD i;
553 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
555 WCHAR wszCachePath[MAX_PATH];
556 WCHAR wszMutexName[MAX_PATH];
557 int path_len, suffix_len;
559 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
561 ERR("Couldn't get path for default container %u\n", i);
562 continue;
564 path_len = strlenW(wszCachePath);
565 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
567 if (path_len + suffix_len + 2 > MAX_PATH)
569 ERR("Path too long\n");
570 continue;
573 wszCachePath[path_len] = '\\';
574 wszCachePath[path_len+1] = 0;
576 strcpyW(wszMutexName, wszCachePath);
578 if (suffix_len)
580 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
581 wszCachePath[path_len + suffix_len + 1] = '\\';
582 wszCachePath[path_len + suffix_len + 2] = '\0';
585 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
589 void URLCacheContainers_DeleteAll(void)
591 while(!list_empty(&UrlContainers))
592 URLCacheContainer_DeleteContainer(
593 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
597 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
599 URLCACHECONTAINER * pContainer;
601 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
603 if(!lpwszUrl)
604 return ERROR_INVALID_PARAMETER;
606 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
608 int prefix_len = strlenW(pContainer->cache_prefix);
609 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
611 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
612 *ppContainer = pContainer;
613 return ERROR_SUCCESS;
616 ERR("no container found\n");
617 return ERROR_FILE_NOT_FOUND;
620 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
622 LPWSTR url = NULL;
623 DWORD ret;
625 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
626 return ERROR_OUTOFMEMORY;
628 ret = URLCacheContainers_FindContainerW(url, ppContainer);
629 heap_free(url);
630 return ret;
633 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
635 DWORD i = 0;
636 URLCACHECONTAINER * pContainer;
638 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
640 /* non-NULL search pattern only returns one container ever */
641 if (lpwszSearchPattern && dwIndex > 0)
642 return FALSE;
644 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
646 if (lpwszSearchPattern)
648 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
650 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
651 *ppContainer = pContainer;
652 return TRUE;
655 else
657 if (i == dwIndex)
659 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
660 *ppContainer = pContainer;
661 return TRUE;
664 i++;
666 return FALSE;
669 /***********************************************************************
670 * URLCacheContainer_LockIndex (Internal)
672 * Locks the index for system-wide exclusive access.
674 * RETURNS
675 * Cache file header if successful
676 * NULL if failed and calls SetLastError.
678 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
680 BYTE index;
681 LPVOID pIndexData;
682 URLCACHE_HEADER * pHeader;
683 DWORD error;
685 /* acquire mutex */
686 WaitForSingleObject(pContainer->hMutex, INFINITE);
688 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
690 if (!pIndexData)
692 ReleaseMutex(pContainer->hMutex);
693 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
694 return NULL;
696 pHeader = (URLCACHE_HEADER *)pIndexData;
698 /* file has grown - we need to remap to prevent us getting
699 * access violations when we try and access beyond the end
700 * of the memory mapped file */
701 if (pHeader->dwFileSize != pContainer->file_size)
703 UnmapViewOfFile( pHeader );
704 URLCacheContainer_CloseIndex(pContainer);
705 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
706 if (error != ERROR_SUCCESS)
708 ReleaseMutex(pContainer->hMutex);
709 SetLastError(error);
710 return NULL;
712 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
714 if (!pIndexData)
716 ReleaseMutex(pContainer->hMutex);
717 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
718 return NULL;
720 pHeader = (URLCACHE_HEADER *)pIndexData;
723 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
725 for (index = 0; index < pHeader->DirectoryCount; index++)
727 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
730 return pHeader;
733 /***********************************************************************
734 * URLCacheContainer_UnlockIndex (Internal)
737 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
739 /* release mutex */
740 ReleaseMutex(pContainer->hMutex);
741 return UnmapViewOfFile(pHeader);
744 /***********************************************************************
745 * URLCacheContainer_CleanIndex (Internal)
747 * This function is meant to make place in index file by removing old
748 * entries and resizing the file.
750 * CAUTION: file view may get mapped to new memory
751 * TODO: implement entries cleaning
753 * RETURNS
754 * ERROR_SUCCESS when new memory is available
755 * error code otherwise
757 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
759 URLCACHE_HEADER *header = *file_view;
760 DWORD ret;
762 FIXME("(%s %s) semi-stub\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
764 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
765 WARN("index file has maximal size\n");
766 return ERROR_NOT_ENOUGH_MEMORY;
769 URLCacheContainer_CloseIndex(container);
770 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
771 if(ret != ERROR_SUCCESS)
772 return ret;
773 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
774 if(!header)
775 return GetLastError();
777 UnmapViewOfFile(*file_view);
778 *file_view = header;
779 return ERROR_SUCCESS;
782 #ifndef CHAR_BIT
783 #define CHAR_BIT (8 * sizeof(CHAR))
784 #endif
786 /***********************************************************************
787 * URLCache_Allocation_BlockIsFree (Internal)
789 * Is the specified block number free?
791 * RETURNS
792 * zero if free
793 * non-zero otherwise
796 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
798 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
799 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
802 /***********************************************************************
803 * URLCache_Allocation_BlockFree (Internal)
805 * Marks the specified block as free
807 * RETURNS
808 * nothing
811 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
813 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
814 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
817 /***********************************************************************
818 * URLCache_Allocation_BlockAllocate (Internal)
820 * Marks the specified block as allocated
822 * RETURNS
823 * nothing
826 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
828 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
829 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
832 /***********************************************************************
833 * URLCache_FindFirstFreeEntry (Internal)
835 * Finds and allocates the first block of free space big enough and
836 * sets ppEntry to point to it.
838 * RETURNS
839 * ERROR_SUCCESS when free memory block was found
840 * Any other Win32 error code if the entry could not be added
843 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
845 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
846 DWORD dwBlockNumber;
847 DWORD dwFreeCounter;
848 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
850 for (dwFreeCounter = 0;
851 dwFreeCounter < dwBlocksNeeded &&
852 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
853 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
854 dwFreeCounter++)
855 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
857 if (dwFreeCounter == dwBlocksNeeded)
859 DWORD index;
860 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
861 for (index = 0; index < dwBlocksNeeded; index++)
862 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
863 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
864 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
865 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
866 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
867 return ERROR_SUCCESS;
871 return ERROR_HANDLE_DISK_FULL;
874 /***********************************************************************
875 * URLCache_DeleteEntry (Internal)
877 * Deletes the specified entry and frees the space allocated to it
879 * RETURNS
880 * TRUE if it succeeded
881 * FALSE if it failed
884 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
886 DWORD dwStartBlock;
887 DWORD dwBlock;
888 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
890 /* update allocation table */
891 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
892 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
893 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
895 return TRUE;
898 /***********************************************************************
899 * URLCache_LocalFileNameToPathW (Internal)
901 * Copies the full path to the specified buffer given the local file
902 * name and the index of the directory it is in. Always sets value in
903 * lpBufferSize to the required buffer size (in bytes).
905 * RETURNS
906 * TRUE if the buffer was big enough
907 * FALSE if the buffer was too small
910 static BOOL URLCache_LocalFileNameToPathW(
911 const URLCACHECONTAINER * pContainer,
912 LPCURLCACHE_HEADER pHeader,
913 LPCSTR szLocalFileName,
914 BYTE Directory,
915 LPWSTR wszPath,
916 LPLONG lpBufferSize)
918 LONG nRequired;
919 int path_len = strlenW(pContainer->path);
920 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
921 if (Directory >= pHeader->DirectoryCount)
923 *lpBufferSize = 0;
924 return FALSE;
927 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
928 if (nRequired <= *lpBufferSize)
930 int dir_len;
932 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
933 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
934 wszPath[dir_len + path_len] = '\\';
935 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
936 *lpBufferSize = nRequired;
937 return TRUE;
939 *lpBufferSize = nRequired;
940 return FALSE;
943 /***********************************************************************
944 * URLCache_LocalFileNameToPathA (Internal)
946 * Copies the full path to the specified buffer given the local file
947 * name and the index of the directory it is in. Always sets value in
948 * lpBufferSize to the required buffer size.
950 * RETURNS
951 * TRUE if the buffer was big enough
952 * FALSE if the buffer was too small
955 static BOOL URLCache_LocalFileNameToPathA(
956 const URLCACHECONTAINER * pContainer,
957 LPCURLCACHE_HEADER pHeader,
958 LPCSTR szLocalFileName,
959 BYTE Directory,
960 LPSTR szPath,
961 LPLONG lpBufferSize)
963 LONG nRequired;
964 int path_len, file_name_len, dir_len;
966 if (Directory >= pHeader->DirectoryCount)
968 *lpBufferSize = 0;
969 return FALSE;
972 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
973 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
974 dir_len = DIR_LENGTH;
976 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
977 if (nRequired < *lpBufferSize)
979 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
980 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
981 szPath[path_len + dir_len] = '\\';
982 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
983 *lpBufferSize = nRequired;
984 return TRUE;
986 *lpBufferSize = nRequired;
987 return FALSE;
990 /* Just like DosDateTimeToFileTime, except that it also maps the special
991 * case of a DOS date/time of (0,0) to a filetime of (0,0).
993 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
994 FILETIME *ft)
996 if (!fatdate && !fattime)
997 ft->dwLowDateTime = ft->dwHighDateTime = 0;
998 else
999 DosDateTimeToFileTime(fatdate, fattime, ft);
1002 /***********************************************************************
1003 * URLCache_CopyEntry (Internal)
1005 * Copies an entry from the cache index file to the Win32 structure
1007 * RETURNS
1008 * ERROR_SUCCESS if the buffer was big enough
1009 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1012 static DWORD URLCache_CopyEntry(
1013 URLCACHECONTAINER * pContainer,
1014 LPCURLCACHE_HEADER pHeader,
1015 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1016 LPDWORD lpdwBufferSize,
1017 const URL_CACHEFILE_ENTRY * pUrlEntry,
1018 BOOL bUnicode)
1020 int lenUrl;
1021 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1023 if (*lpdwBufferSize >= dwRequiredSize)
1025 lpCacheEntryInfo->lpHeaderInfo = NULL;
1026 lpCacheEntryInfo->lpszFileExtension = NULL;
1027 lpCacheEntryInfo->lpszLocalFileName = NULL;
1028 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1029 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1030 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1031 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1032 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1033 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1034 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1035 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1036 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1037 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1038 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1039 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1040 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1041 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1042 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1045 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1046 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1047 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1048 if (bUnicode)
1049 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1050 else
1051 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1052 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1054 /* FIXME: is source url optional? */
1055 if (*lpdwBufferSize >= dwRequiredSize)
1057 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1059 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1060 if (bUnicode)
1061 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1062 else
1063 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1066 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1067 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1068 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1070 if (pUrlEntry->dwOffsetLocalName)
1072 LONG nLocalFilePathSize;
1073 LPSTR lpszLocalFileName;
1074 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1075 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1076 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1077 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1079 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1081 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1083 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1084 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1085 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1087 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1089 if (*lpdwBufferSize >= dwRequiredSize)
1091 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1092 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1093 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1095 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1096 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1097 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1099 if (pUrlEntry->dwOffsetFileExtension)
1101 int lenExtension;
1103 if (bUnicode)
1104 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1105 else
1106 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1107 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1109 if (*lpdwBufferSize >= dwRequiredSize)
1111 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1112 if (bUnicode)
1113 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1114 else
1115 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1118 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1119 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1120 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1123 if (dwRequiredSize > *lpdwBufferSize)
1125 *lpdwBufferSize = dwRequiredSize;
1126 return ERROR_INSUFFICIENT_BUFFER;
1128 *lpdwBufferSize = dwRequiredSize;
1129 return ERROR_SUCCESS;
1132 /* Just like FileTimeToDosDateTime, except that it also maps the special
1133 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1135 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1136 WORD *fattime)
1138 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1139 *fatdate = *fattime = 0;
1140 else
1141 FileTimeToDosDateTime(ft, fatdate, fattime);
1144 /***********************************************************************
1145 * URLCache_SetEntryInfo (Internal)
1147 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1148 * according to the flags set by dwFieldControl.
1150 * RETURNS
1151 * ERROR_SUCCESS if the buffer was big enough
1152 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1155 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1157 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1158 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1159 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1160 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1161 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1162 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1163 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1164 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1165 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1166 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1167 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1168 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1169 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1170 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1171 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1172 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1174 return ERROR_SUCCESS;
1177 /***********************************************************************
1178 * URLCache_HashKey (Internal)
1180 * Returns the hash key for a given string
1182 * RETURNS
1183 * hash key for the string
1186 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1188 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1189 * but the algorithm and result are not the same!
1191 static const unsigned char lookupTable[256] =
1193 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1194 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1195 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1196 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1197 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1198 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1199 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1200 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1201 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1202 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1203 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1204 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1205 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1206 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1207 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1208 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1209 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1210 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1211 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1212 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1213 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1214 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1215 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1216 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1217 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1218 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1219 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1220 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1221 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1222 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1223 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1224 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1226 BYTE key[4];
1227 DWORD i;
1229 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1230 key[i] = lookupTable[i];
1232 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1234 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1235 key[i] = lookupTable[*lpszKey ^ key[i]];
1238 return *(DWORD *)key;
1241 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1243 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1246 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1248 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1249 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1250 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1253 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1255 /* structure of hash table:
1256 * 448 entries divided into 64 blocks
1257 * each block therefore contains a chain of 7 key/offset pairs
1258 * how position in table is calculated:
1259 * 1. the url is hashed in helper function
1260 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1261 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1263 * note:
1264 * there can be multiple hash tables in the file and the offset to
1265 * the next one is stored in the header of the hash table
1267 DWORD key = URLCache_HashKey(lpszUrl);
1268 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1269 HASH_CACHEFILE_ENTRY * pHashEntry;
1270 DWORD dwHashTableNumber = 0;
1272 key >>= HASHTABLE_FLAG_BITS;
1274 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1275 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1276 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1278 int i;
1279 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1281 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1282 continue;
1284 /* make sure that it is in fact a hash entry */
1285 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1287 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1288 continue;
1291 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1293 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1294 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1296 /* FIXME: we should make sure that this is the right element
1297 * before returning and claiming that it is. We can do this
1298 * by doing a simple compare between the URL we were given
1299 * and the URL stored in the entry. However, this assumes
1300 * we know the format of all the entries stored in the
1301 * hash table */
1302 *ppHashEntry = pHashElement;
1303 return TRUE;
1307 return FALSE;
1310 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1312 LPSTR urlA;
1313 BOOL ret;
1315 urlA = heap_strdupWtoA(lpszUrl);
1316 if (!urlA)
1318 SetLastError(ERROR_OUTOFMEMORY);
1319 return FALSE;
1322 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1323 heap_free(urlA);
1324 return ret;
1327 /***********************************************************************
1328 * URLCache_HashEntrySetFlags (Internal)
1330 * Sets special bits in hash key
1332 * RETURNS
1333 * nothing
1336 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1338 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1341 /***********************************************************************
1342 * URLCache_DeleteEntryFromHash (Internal)
1344 * Searches all the hash tables in the index for the given URL and
1345 * then if found deletes the entry.
1347 * RETURNS
1348 * TRUE if the entry was found
1349 * FALSE if the entry could not be found
1352 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1354 pHashEntry->dwHashKey = HASHTABLE_DEL;
1355 return TRUE;
1358 /***********************************************************************
1359 * URLCache_AddEntryToHash (Internal)
1361 * Searches all the hash tables for a free slot based on the offset
1362 * generated from the hash key. If a free slot is found, the offset and
1363 * key are entered into the hash table.
1365 * RETURNS
1366 * ERROR_SUCCESS if the entry was added
1367 * Any other Win32 error code if the entry could not be added
1370 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1372 /* see URLCache_FindEntryInHash for structure of hash tables */
1374 DWORD key = URLCache_HashKey(lpszUrl);
1375 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1376 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1377 DWORD dwHashTableNumber = 0;
1378 DWORD error;
1380 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1382 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1383 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1384 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1386 int i;
1387 pHashPrev = pHashEntry;
1389 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1391 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1392 break;
1394 /* make sure that it is in fact a hash entry */
1395 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1397 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1398 break;
1401 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1403 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1404 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1406 pHashElement->dwHashKey = key;
1407 pHashElement->dwOffsetEntry = dwOffsetEntry;
1408 return ERROR_SUCCESS;
1412 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1413 if (error != ERROR_SUCCESS)
1414 return error;
1416 pHashEntry->HashTable[offset].dwHashKey = key;
1417 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1418 return ERROR_SUCCESS;
1421 /***********************************************************************
1422 * URLCache_CreateHashTable (Internal)
1424 * Creates a new hash table in free space and adds it to the chain of existing
1425 * hash tables.
1427 * RETURNS
1428 * ERROR_SUCCESS if the hash table was created
1429 * ERROR_DISK_FULL if the hash table could not be created
1432 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1434 DWORD dwOffset, error;
1435 int i;
1437 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1438 return error;
1440 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1442 if (pPrevHash)
1443 pPrevHash->dwAddressNext = dwOffset;
1444 else
1445 pHeader->dwOffsetFirstHashTable = dwOffset;
1446 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1447 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1448 (*ppHash)->dwAddressNext = 0;
1449 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1450 for (i = 0; i < HASHTABLE_SIZE; i++)
1452 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1453 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1455 return ERROR_SUCCESS;
1458 /***********************************************************************
1459 * URLCache_EnumHashTables (Internal)
1461 * Enumerates the hash tables in a container.
1463 * RETURNS
1464 * TRUE if an entry was found
1465 * FALSE if there are no more tables to enumerate.
1468 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1470 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1471 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1472 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1474 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1475 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1476 continue;
1477 /* make sure that it is in fact a hash entry */
1478 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1480 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1481 (*pdwHashTableNumber)++;
1482 continue;
1485 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1486 return TRUE;
1488 return FALSE;
1491 /***********************************************************************
1492 * URLCache_EnumHashTableEntries (Internal)
1494 * Enumerates entries in a hash table and returns the next non-free entry.
1496 * RETURNS
1497 * TRUE if an entry was found
1498 * FALSE if the hash table is empty or there are no more entries to
1499 * enumerate.
1502 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1503 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1505 for (; *index < HASHTABLE_SIZE ; (*index)++)
1507 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1508 continue;
1510 *ppHashEntry = &pHashEntry->HashTable[*index];
1511 TRACE("entry found %d\n", *index);
1512 return TRUE;
1514 TRACE("no more entries (%d)\n", *index);
1515 return FALSE;
1518 /***********************************************************************
1519 * URLCache_DeleteCacheDirectory (Internal)
1521 * Erase a directory containing an URL cache.
1523 * RETURNS
1524 * TRUE success, FALSE failure/aborted.
1527 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1529 DWORD path_len;
1530 WCHAR path[MAX_PATH + 1];
1531 SHFILEOPSTRUCTW shfos;
1532 int ret;
1534 path_len = strlenW(lpszPath);
1535 if (path_len >= MAX_PATH)
1536 return FALSE;
1537 strcpyW(path, lpszPath);
1538 path[path_len + 1] = 0; /* double-NUL-terminate path */
1540 shfos.hwnd = NULL;
1541 shfos.wFunc = FO_DELETE;
1542 shfos.pFrom = path;
1543 shfos.pTo = NULL;
1544 shfos.fFlags = 0;
1545 shfos.fAnyOperationsAborted = FALSE;
1546 ret = SHFileOperationW(&shfos);
1547 if (ret)
1548 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1549 return !(ret || shfos.fAnyOperationsAborted);
1552 /***********************************************************************
1553 * FreeUrlCacheSpaceW (WININET.@)
1555 * Frees up some cache.
1557 * PARAMETERS
1558 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1559 * dwSize [I] How much space to free up.
1560 * dwSizeType [I] How to interpret dwSize.
1562 * RETURNS
1563 * TRUE success. FALSE failure.
1565 * IMPLEMENTATION
1566 * This implementation just retrieves the path of the cache directory, and
1567 * deletes its contents from the filesystem. The correct approach would
1568 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1570 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1572 URLCACHECONTAINER * pContainer;
1574 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1576 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1577 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1578 return FALSE;
1581 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1583 /* The URL cache has prefix L"" (unlike Cookies and History) */
1584 if (pContainer->cache_prefix[0] == 0)
1586 BOOL ret_del;
1587 DWORD ret_open;
1588 WaitForSingleObject(pContainer->hMutex, INFINITE);
1590 /* unlock, delete, recreate and lock cache */
1591 URLCacheContainer_CloseIndex(pContainer);
1592 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1593 ret_open = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1595 ReleaseMutex(pContainer->hMutex);
1596 return ret_del && (ret_open == ERROR_SUCCESS);
1599 return FALSE;
1602 /***********************************************************************
1603 * FreeUrlCacheSpaceA (WININET.@)
1605 * See FreeUrlCacheSpaceW.
1607 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1609 BOOL ret = FALSE;
1610 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1611 if (lpszCachePath == NULL || path != NULL)
1612 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1613 heap_free(path);
1614 return ret;
1617 /***********************************************************************
1618 * GetUrlCacheEntryInfoExA (WININET.@)
1621 BOOL WINAPI GetUrlCacheEntryInfoExA(
1622 LPCSTR lpszUrl,
1623 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1624 LPDWORD lpdwCacheEntryInfoBufSize,
1625 LPSTR lpszReserved,
1626 LPDWORD lpdwReserved,
1627 LPVOID lpReserved,
1628 DWORD dwFlags)
1630 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1631 debugstr_a(lpszUrl),
1632 lpCacheEntryInfo,
1633 lpdwCacheEntryInfoBufSize,
1634 lpszReserved,
1635 lpdwReserved,
1636 lpReserved,
1637 dwFlags);
1639 if ((lpszReserved != NULL) ||
1640 (lpdwReserved != NULL) ||
1641 (lpReserved != NULL))
1643 ERR("Reserved value was not 0\n");
1644 SetLastError(ERROR_INVALID_PARAMETER);
1645 return FALSE;
1647 if (dwFlags != 0)
1649 FIXME("Undocumented flag(s): %x\n", dwFlags);
1650 SetLastError(ERROR_FILE_NOT_FOUND);
1651 return FALSE;
1653 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1656 /***********************************************************************
1657 * GetUrlCacheEntryInfoA (WININET.@)
1660 BOOL WINAPI GetUrlCacheEntryInfoA(
1661 IN LPCSTR lpszUrlName,
1662 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1663 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1666 LPURLCACHE_HEADER pHeader;
1667 struct _HASH_ENTRY * pHashEntry;
1668 const CACHEFILE_ENTRY * pEntry;
1669 const URL_CACHEFILE_ENTRY * pUrlEntry;
1670 URLCACHECONTAINER * pContainer;
1671 DWORD error;
1673 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1675 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1676 if (error != ERROR_SUCCESS)
1678 SetLastError(error);
1679 return FALSE;
1682 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1683 if (error != ERROR_SUCCESS)
1685 SetLastError(error);
1686 return FALSE;
1689 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1690 return FALSE;
1692 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1694 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1695 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1696 SetLastError(ERROR_FILE_NOT_FOUND);
1697 return FALSE;
1700 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1701 if (pEntry->dwSignature != URL_SIGNATURE)
1703 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1704 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1705 SetLastError(ERROR_FILE_NOT_FOUND);
1706 return FALSE;
1709 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1710 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1711 if (pUrlEntry->dwOffsetHeaderInfo)
1712 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1714 if (lpdwCacheEntryInfoBufferSize)
1716 if (!lpCacheEntryInfo)
1717 *lpdwCacheEntryInfoBufferSize = 0;
1719 error = URLCache_CopyEntry(
1720 pContainer,
1721 pHeader,
1722 lpCacheEntryInfo,
1723 lpdwCacheEntryInfoBufferSize,
1724 pUrlEntry,
1725 FALSE /* ANSI */);
1726 if (error != ERROR_SUCCESS)
1728 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1729 SetLastError(error);
1730 return FALSE;
1732 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1735 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1737 return TRUE;
1740 /***********************************************************************
1741 * GetUrlCacheEntryInfoW (WININET.@)
1744 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1745 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1746 LPDWORD lpdwCacheEntryInfoBufferSize)
1748 LPURLCACHE_HEADER pHeader;
1749 struct _HASH_ENTRY * pHashEntry;
1750 const CACHEFILE_ENTRY * pEntry;
1751 const URL_CACHEFILE_ENTRY * pUrlEntry;
1752 URLCACHECONTAINER * pContainer;
1753 DWORD error;
1755 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1757 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1758 if (error != ERROR_SUCCESS)
1760 SetLastError(error);
1761 return FALSE;
1764 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1765 if (error != ERROR_SUCCESS)
1767 SetLastError(error);
1768 return FALSE;
1771 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1772 return FALSE;
1774 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1776 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1777 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1778 SetLastError(ERROR_FILE_NOT_FOUND);
1779 return FALSE;
1782 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1783 if (pEntry->dwSignature != URL_SIGNATURE)
1785 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1786 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1787 SetLastError(ERROR_FILE_NOT_FOUND);
1788 return FALSE;
1791 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1792 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1793 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1795 if (lpdwCacheEntryInfoBufferSize)
1797 if (!lpCacheEntryInfo)
1798 *lpdwCacheEntryInfoBufferSize = 0;
1800 error = URLCache_CopyEntry(
1801 pContainer,
1802 pHeader,
1803 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1804 lpdwCacheEntryInfoBufferSize,
1805 pUrlEntry,
1806 TRUE /* UNICODE */);
1807 if (error != ERROR_SUCCESS)
1809 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1810 SetLastError(error);
1811 return FALSE;
1813 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1816 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1818 return TRUE;
1821 /***********************************************************************
1822 * GetUrlCacheEntryInfoExW (WININET.@)
1825 BOOL WINAPI GetUrlCacheEntryInfoExW(
1826 LPCWSTR lpszUrl,
1827 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1828 LPDWORD lpdwCacheEntryInfoBufSize,
1829 LPWSTR lpszReserved,
1830 LPDWORD lpdwReserved,
1831 LPVOID lpReserved,
1832 DWORD dwFlags)
1834 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1835 debugstr_w(lpszUrl),
1836 lpCacheEntryInfo,
1837 lpdwCacheEntryInfoBufSize,
1838 lpszReserved,
1839 lpdwReserved,
1840 lpReserved,
1841 dwFlags);
1843 if ((lpszReserved != NULL) ||
1844 (lpdwReserved != NULL) ||
1845 (lpReserved != NULL))
1847 ERR("Reserved value was not 0\n");
1848 SetLastError(ERROR_INVALID_PARAMETER);
1849 return FALSE;
1851 if (dwFlags != 0)
1853 FIXME("Undocumented flag(s): %x\n", dwFlags);
1854 SetLastError(ERROR_FILE_NOT_FOUND);
1855 return FALSE;
1857 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1860 /***********************************************************************
1861 * SetUrlCacheEntryInfoA (WININET.@)
1863 BOOL WINAPI SetUrlCacheEntryInfoA(
1864 LPCSTR lpszUrlName,
1865 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1866 DWORD dwFieldControl)
1868 LPURLCACHE_HEADER pHeader;
1869 struct _HASH_ENTRY * pHashEntry;
1870 CACHEFILE_ENTRY * pEntry;
1871 URLCACHECONTAINER * pContainer;
1872 DWORD error;
1874 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1876 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1877 if (error != ERROR_SUCCESS)
1879 SetLastError(error);
1880 return FALSE;
1883 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1884 if (error != ERROR_SUCCESS)
1886 SetLastError(error);
1887 return FALSE;
1890 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1891 return FALSE;
1893 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1895 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1896 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1897 SetLastError(ERROR_FILE_NOT_FOUND);
1898 return FALSE;
1901 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1902 if (pEntry->dwSignature != URL_SIGNATURE)
1904 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1905 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1906 SetLastError(ERROR_FILE_NOT_FOUND);
1907 return FALSE;
1910 URLCache_SetEntryInfo(
1911 (URL_CACHEFILE_ENTRY *)pEntry,
1912 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1913 dwFieldControl);
1915 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1917 return TRUE;
1920 /***********************************************************************
1921 * SetUrlCacheEntryInfoW (WININET.@)
1923 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1925 LPURLCACHE_HEADER pHeader;
1926 struct _HASH_ENTRY * pHashEntry;
1927 CACHEFILE_ENTRY * pEntry;
1928 URLCACHECONTAINER * pContainer;
1929 DWORD error;
1931 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1933 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1934 if (error != ERROR_SUCCESS)
1936 SetLastError(error);
1937 return FALSE;
1940 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1941 if (error != ERROR_SUCCESS)
1943 SetLastError(error);
1944 return FALSE;
1947 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1948 return FALSE;
1950 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1952 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1953 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1954 SetLastError(ERROR_FILE_NOT_FOUND);
1955 return FALSE;
1958 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1959 if (pEntry->dwSignature != URL_SIGNATURE)
1961 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1962 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1963 SetLastError(ERROR_FILE_NOT_FOUND);
1964 return FALSE;
1967 URLCache_SetEntryInfo(
1968 (URL_CACHEFILE_ENTRY *)pEntry,
1969 lpCacheEntryInfo,
1970 dwFieldControl);
1972 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1974 return TRUE;
1977 /***********************************************************************
1978 * RetrieveUrlCacheEntryFileA (WININET.@)
1981 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1982 IN LPCSTR lpszUrlName,
1983 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1984 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1985 IN DWORD dwReserved
1988 LPURLCACHE_HEADER pHeader;
1989 struct _HASH_ENTRY * pHashEntry;
1990 CACHEFILE_ENTRY * pEntry;
1991 URL_CACHEFILE_ENTRY * pUrlEntry;
1992 URLCACHECONTAINER * pContainer;
1993 DWORD error;
1995 TRACE("(%s, %p, %p, 0x%08x)\n",
1996 debugstr_a(lpszUrlName),
1997 lpCacheEntryInfo,
1998 lpdwCacheEntryInfoBufferSize,
1999 dwReserved);
2001 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2002 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2004 SetLastError(ERROR_INVALID_PARAMETER);
2005 return FALSE;
2008 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2009 if (error != ERROR_SUCCESS)
2011 SetLastError(error);
2012 return FALSE;
2015 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2016 if (error != ERROR_SUCCESS)
2018 SetLastError(error);
2019 return FALSE;
2022 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2023 return FALSE;
2025 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2027 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2028 TRACE("entry %s not found!\n", lpszUrlName);
2029 SetLastError(ERROR_FILE_NOT_FOUND);
2030 return FALSE;
2033 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2034 if (pEntry->dwSignature != URL_SIGNATURE)
2036 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2037 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2038 SetLastError(ERROR_FILE_NOT_FOUND);
2039 return FALSE;
2042 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2043 if (!pUrlEntry->dwOffsetLocalName)
2045 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2046 SetLastError(ERROR_INVALID_DATA);
2047 return FALSE;
2050 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2051 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2053 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2054 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2055 FALSE);
2056 if (error != ERROR_SUCCESS)
2058 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2059 SetLastError(error);
2060 return FALSE;
2062 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2064 pUrlEntry->dwHitRate++;
2065 pUrlEntry->dwUseCount++;
2066 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2067 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2069 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2071 return TRUE;
2074 /***********************************************************************
2075 * RetrieveUrlCacheEntryFileW (WININET.@)
2078 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2079 IN LPCWSTR lpszUrlName,
2080 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2081 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2082 IN DWORD dwReserved
2085 LPURLCACHE_HEADER pHeader;
2086 struct _HASH_ENTRY * pHashEntry;
2087 CACHEFILE_ENTRY * pEntry;
2088 URL_CACHEFILE_ENTRY * pUrlEntry;
2089 URLCACHECONTAINER * pContainer;
2090 DWORD error;
2092 TRACE("(%s, %p, %p, 0x%08x)\n",
2093 debugstr_w(lpszUrlName),
2094 lpCacheEntryInfo,
2095 lpdwCacheEntryInfoBufferSize,
2096 dwReserved);
2098 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2099 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2101 SetLastError(ERROR_INVALID_PARAMETER);
2102 return FALSE;
2105 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2106 if (error != ERROR_SUCCESS)
2108 SetLastError(error);
2109 return FALSE;
2112 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2113 if (error != ERROR_SUCCESS)
2115 SetLastError(error);
2116 return FALSE;
2119 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2120 return FALSE;
2122 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2124 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2125 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2126 SetLastError(ERROR_FILE_NOT_FOUND);
2127 return FALSE;
2130 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2131 if (pEntry->dwSignature != URL_SIGNATURE)
2133 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2134 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2135 SetLastError(ERROR_FILE_NOT_FOUND);
2136 return FALSE;
2139 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2140 if (!pUrlEntry->dwOffsetLocalName)
2142 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2143 SetLastError(ERROR_INVALID_DATA);
2144 return FALSE;
2147 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2148 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2150 error = URLCache_CopyEntry(
2151 pContainer,
2152 pHeader,
2153 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2154 lpdwCacheEntryInfoBufferSize,
2155 pUrlEntry,
2156 TRUE /* UNICODE */);
2157 if (error != ERROR_SUCCESS)
2159 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2160 SetLastError(error);
2161 return FALSE;
2163 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2165 pUrlEntry->dwHitRate++;
2166 pUrlEntry->dwUseCount++;
2167 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2168 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2170 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2172 return TRUE;
2175 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2176 struct _HASH_ENTRY *pHashEntry)
2178 CACHEFILE_ENTRY * pEntry;
2179 URL_CACHEFILE_ENTRY * pUrlEntry;
2181 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2182 if (pEntry->dwSignature != URL_SIGNATURE)
2184 FIXME("Trying to delete entry of unknown format %s\n",
2185 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2186 SetLastError(ERROR_FILE_NOT_FOUND);
2187 return FALSE;
2190 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2191 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2193 /* FIXME: implement timeout object unlocking */
2194 TRACE("Trying to delete locked entry\n");
2195 pUrlEntry->CacheEntryType |= DELETED_CACHE_ENTRY;
2196 SetLastError(ERROR_SHARING_VIOLATION);
2197 return FALSE;
2200 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2202 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2203 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2205 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2207 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2208 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2209 else
2210 pHeader->ExemptUsage.QuadPart = 0;
2212 else
2214 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2215 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2216 else
2217 pHeader->CacheUsage.QuadPart = 0;
2220 URLCache_DeleteEntry(pHeader, pEntry);
2222 URLCache_DeleteEntryFromHash(pHashEntry);
2223 return TRUE;
2226 /***********************************************************************
2227 * UnlockUrlCacheEntryFileA (WININET.@)
2230 BOOL WINAPI UnlockUrlCacheEntryFileA(
2231 IN LPCSTR lpszUrlName,
2232 IN DWORD dwReserved
2235 LPURLCACHE_HEADER pHeader;
2236 struct _HASH_ENTRY * pHashEntry;
2237 CACHEFILE_ENTRY * pEntry;
2238 URL_CACHEFILE_ENTRY * pUrlEntry;
2239 URLCACHECONTAINER * pContainer;
2240 DWORD error;
2242 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2244 if (dwReserved)
2246 ERR("dwReserved != 0\n");
2247 SetLastError(ERROR_INVALID_PARAMETER);
2248 return FALSE;
2251 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2252 if (error != ERROR_SUCCESS)
2254 SetLastError(error);
2255 return FALSE;
2258 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2259 if (error != ERROR_SUCCESS)
2261 SetLastError(error);
2262 return FALSE;
2265 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2266 return FALSE;
2268 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2270 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2271 TRACE("entry %s not found!\n", lpszUrlName);
2272 SetLastError(ERROR_FILE_NOT_FOUND);
2273 return FALSE;
2276 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2277 if (pEntry->dwSignature != URL_SIGNATURE)
2279 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2280 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2281 SetLastError(ERROR_FILE_NOT_FOUND);
2282 return FALSE;
2285 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2287 if (pUrlEntry->dwUseCount == 0)
2289 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2290 return FALSE;
2292 pUrlEntry->dwUseCount--;
2293 if (!pUrlEntry->dwUseCount)
2295 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2296 if (pUrlEntry->CacheEntryType & DELETED_CACHE_ENTRY)
2297 DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
2300 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2302 return TRUE;
2305 /***********************************************************************
2306 * UnlockUrlCacheEntryFileW (WININET.@)
2309 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2311 LPURLCACHE_HEADER pHeader;
2312 struct _HASH_ENTRY * pHashEntry;
2313 CACHEFILE_ENTRY * pEntry;
2314 URL_CACHEFILE_ENTRY * pUrlEntry;
2315 URLCACHECONTAINER * pContainer;
2316 DWORD error;
2318 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2320 if (dwReserved)
2322 ERR("dwReserved != 0\n");
2323 SetLastError(ERROR_INVALID_PARAMETER);
2324 return FALSE;
2327 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2328 if (error != ERROR_SUCCESS)
2330 SetLastError(error);
2331 return FALSE;
2334 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2335 if (error != ERROR_SUCCESS)
2337 SetLastError(error);
2338 return FALSE;
2341 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2342 return FALSE;
2344 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2346 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2347 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2348 SetLastError(ERROR_FILE_NOT_FOUND);
2349 return FALSE;
2352 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2353 if (pEntry->dwSignature != URL_SIGNATURE)
2355 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2356 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2357 SetLastError(ERROR_FILE_NOT_FOUND);
2358 return FALSE;
2361 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2363 if (pUrlEntry->dwUseCount == 0)
2365 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2366 return FALSE;
2368 pUrlEntry->dwUseCount--;
2369 if (!pUrlEntry->dwUseCount)
2370 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2372 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2374 return TRUE;
2377 /***********************************************************************
2378 * CreateUrlCacheEntryA (WININET.@)
2381 BOOL WINAPI CreateUrlCacheEntryA(
2382 IN LPCSTR lpszUrlName,
2383 IN DWORD dwExpectedFileSize,
2384 IN LPCSTR lpszFileExtension,
2385 OUT LPSTR lpszFileName,
2386 IN DWORD dwReserved
2389 WCHAR *url_name;
2390 WCHAR *file_extension = NULL;
2391 WCHAR file_name[MAX_PATH];
2392 BOOL bSuccess = FALSE;
2393 DWORD dwError = 0;
2395 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2396 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2398 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2400 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2402 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2404 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2406 bSuccess = TRUE;
2408 else
2410 dwError = GetLastError();
2413 else
2415 dwError = GetLastError();
2417 heap_free(file_extension);
2419 else
2421 dwError = GetLastError();
2423 heap_free(url_name);
2424 if (!bSuccess) SetLastError(dwError);
2426 return bSuccess;
2428 /***********************************************************************
2429 * CreateUrlCacheEntryW (WININET.@)
2432 BOOL WINAPI CreateUrlCacheEntryW(
2433 IN LPCWSTR lpszUrlName,
2434 IN DWORD dwExpectedFileSize,
2435 IN LPCWSTR lpszFileExtension,
2436 OUT LPWSTR lpszFileName,
2437 IN DWORD dwReserved
2440 URLCACHECONTAINER * pContainer;
2441 LPURLCACHE_HEADER pHeader;
2442 CHAR szFile[MAX_PATH];
2443 WCHAR szExtension[MAX_PATH];
2444 LPCWSTR lpszUrlPart;
2445 LPCWSTR lpszUrlEnd;
2446 LPCWSTR lpszFileNameExtension;
2447 LPWSTR lpszFileNameNoPath;
2448 int i;
2449 int countnoextension;
2450 BYTE CacheDir;
2451 LONG lBufferSize;
2452 BOOL bFound = FALSE;
2453 int count;
2454 DWORD error;
2455 HANDLE hFile;
2456 FILETIME ft;
2458 static const WCHAR szWWW[] = {'w','w','w',0};
2459 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2461 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2462 debugstr_w(lpszUrlName),
2463 dwExpectedFileSize,
2464 debugstr_w(lpszFileExtension),
2465 lpszFileName,
2466 dwReserved);
2468 if (dwReserved)
2469 FIXME("dwReserved 0x%08x\n", dwReserved);
2471 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2473 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2474 lpszUrlEnd--;
2476 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2477 if (!lpszUrlPart)
2478 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2479 if (lpszUrlPart)
2480 lpszUrlEnd = lpszUrlPart;
2482 for (lpszUrlPart = lpszUrlEnd;
2483 (lpszUrlPart >= lpszUrlName);
2484 lpszUrlPart--)
2486 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2488 bFound = TRUE;
2489 lpszUrlPart++;
2490 break;
2493 if (!lstrcmpW(lpszUrlPart, szWWW))
2495 lpszUrlPart += lstrlenW(szWWW);
2498 count = lpszUrlEnd - lpszUrlPart;
2500 if (bFound && (count < MAX_PATH))
2502 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2503 if (!len)
2504 return FALSE;
2505 szFile[len] = '\0';
2506 while(len && szFile[--len] == '/') szFile[len] = '\0';
2508 /* FIXME: get rid of illegal characters like \, / and : */
2510 else
2512 FIXME("need to generate a random filename\n");
2515 TRACE("File name: %s\n", debugstr_a(szFile));
2517 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2518 if (error != ERROR_SUCCESS)
2520 SetLastError(error);
2521 return FALSE;
2524 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2525 if (error != ERROR_SUCCESS)
2527 SetLastError(error);
2528 return FALSE;
2531 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2532 return FALSE;
2534 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2536 lBufferSize = MAX_PATH * sizeof(WCHAR);
2537 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2539 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2540 debugstr_a(szFile), lBufferSize);
2541 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2542 return FALSE;
2545 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2547 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2548 lpszFileNameNoPath >= lpszFileName;
2549 --lpszFileNameNoPath)
2551 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2552 break;
2555 countnoextension = lstrlenW(lpszFileNameNoPath);
2556 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2557 if (lpszFileNameExtension)
2558 countnoextension -= lstrlenW(lpszFileNameExtension);
2559 *szExtension = '\0';
2561 if (lpszFileExtension)
2563 szExtension[0] = '.';
2564 lstrcpyW(szExtension+1, lpszFileExtension);
2567 for (i = 0; i < 255; i++)
2569 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2570 WCHAR *p;
2572 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2573 for (p = lpszFileNameNoPath + 1; *p; p++)
2575 switch (*p)
2577 case '<': case '>':
2578 case ':': case '"':
2579 case '/': case '\\':
2580 case '|': case '?':
2581 case '*':
2582 *p = '_'; break;
2583 default: break;
2586 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2588 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2589 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2590 if (hFile != INVALID_HANDLE_VALUE)
2592 CloseHandle(hFile);
2593 return TRUE;
2597 GetSystemTimeAsFileTime(&ft);
2598 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2600 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2601 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2602 if (hFile != INVALID_HANDLE_VALUE)
2604 CloseHandle(hFile);
2605 return TRUE;
2608 WARN("Could not find a unique filename\n");
2609 return FALSE;
2613 /***********************************************************************
2614 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2616 * The bug we are compensating for is that some drongo at Microsoft
2617 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2618 * As a consequence, CommitUrlCacheEntryA has been effectively
2619 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2620 * is still defined as LPCWSTR. The result (other than madness) is
2621 * that we always need to store lpHeaderInfo in CP_ACP rather than
2622 * in UTF16, and we need to avoid converting lpHeaderInfo in
2623 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2624 * result will lose data for arbitrary binary data.
2627 static BOOL CommitUrlCacheEntryInternal(
2628 IN LPCWSTR lpszUrlName,
2629 IN LPCWSTR lpszLocalFileName,
2630 IN FILETIME ExpireTime,
2631 IN FILETIME LastModifiedTime,
2632 IN DWORD CacheEntryType,
2633 IN LPBYTE lpHeaderInfo,
2634 IN DWORD dwHeaderSize,
2635 IN LPCWSTR lpszFileExtension,
2636 IN LPCWSTR lpszOriginalUrl
2639 URLCACHECONTAINER * pContainer;
2640 LPURLCACHE_HEADER pHeader;
2641 struct _HASH_ENTRY * pHashEntry;
2642 CACHEFILE_ENTRY * pEntry;
2643 URL_CACHEFILE_ENTRY * pUrlEntry;
2644 DWORD url_entry_offset;
2645 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2646 DWORD dwOffsetLocalFileName = 0;
2647 DWORD dwOffsetHeader = 0;
2648 DWORD dwOffsetFileExtension = 0;
2649 LARGE_INTEGER file_size;
2650 BYTE cDirectory = 0;
2651 char achFile[MAX_PATH];
2652 LPSTR lpszUrlNameA = NULL;
2653 LPSTR lpszFileExtensionA = NULL;
2654 char *pchLocalFileName = 0;
2655 DWORD error;
2657 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2658 debugstr_w(lpszUrlName),
2659 debugstr_w(lpszLocalFileName),
2660 CacheEntryType,
2661 lpHeaderInfo,
2662 dwHeaderSize,
2663 debugstr_w(lpszFileExtension),
2664 debugstr_w(lpszOriginalUrl));
2666 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2668 SetLastError(ERROR_INVALID_PARAMETER);
2669 return FALSE;
2671 if (lpszOriginalUrl)
2672 WARN(": lpszOriginalUrl ignored\n");
2674 file_size.QuadPart = 0;
2675 if (lpszLocalFileName)
2677 HANDLE hFile;
2679 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2680 if (hFile == INVALID_HANDLE_VALUE)
2682 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2683 return FALSE;
2686 /* Get file size */
2687 if (!GetFileSizeEx(hFile, &file_size))
2689 ERR("couldn't get file size (error is %d)\n", GetLastError());
2690 CloseHandle(hFile);
2691 return FALSE;
2694 CloseHandle(hFile);
2697 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2698 if (error != ERROR_SUCCESS)
2700 SetLastError(error);
2701 return FALSE;
2704 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2705 if (error != ERROR_SUCCESS)
2707 SetLastError(error);
2708 return FALSE;
2711 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2712 return FALSE;
2714 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2715 if (!lpszUrlNameA)
2717 error = GetLastError();
2718 goto cleanup;
2721 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2723 error = GetLastError();
2724 goto cleanup;
2727 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2729 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2731 /* FIXME: implement timeout object unlocking */
2732 FIXME("Trying to overwrite locked entry\n");
2733 SetLastError(ERROR_SHARING_VIOLATION);
2734 goto cleanup;
2737 FIXME("entry already in cache - don't know what to do!\n");
2739 * SetLastError(ERROR_FILE_NOT_FOUND);
2740 * return FALSE;
2742 goto cleanup;
2745 if (lpszLocalFileName)
2747 BOOL bFound = FALSE;
2749 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2751 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2752 error = ERROR_INVALID_PARAMETER;
2753 goto cleanup;
2756 /* skip container path prefix */
2757 lpszLocalFileName += lstrlenW(pContainer->path);
2759 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2760 pchLocalFileName = achFile;
2762 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2764 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2766 bFound = TRUE;
2767 break;
2771 if (!bFound)
2773 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2774 error = ERROR_INVALID_PARAMETER;
2775 goto cleanup;
2778 lpszLocalFileName += DIR_LENGTH + 1;
2779 pchLocalFileName += DIR_LENGTH + 1;
2782 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2783 if (lpszLocalFileName)
2785 dwOffsetLocalFileName = dwBytesNeeded;
2786 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2788 if (lpHeaderInfo)
2790 dwOffsetHeader = dwBytesNeeded;
2791 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2793 if (lpszFileExtensionA)
2795 dwOffsetFileExtension = dwBytesNeeded;
2796 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2799 /* round up to next block */
2800 if (dwBytesNeeded % BLOCKSIZE)
2802 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2803 dwBytesNeeded += BLOCKSIZE;
2806 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2807 while (error == ERROR_HANDLE_DISK_FULL)
2809 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2810 if (error == ERROR_SUCCESS)
2811 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2813 if (error != ERROR_SUCCESS)
2814 goto cleanup;
2816 /* FindFirstFreeEntry fills in blocks used */
2817 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2818 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
2819 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2820 pUrlEntry->CacheDir = cDirectory;
2821 pUrlEntry->CacheEntryType = CacheEntryType;
2822 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2823 if (CacheEntryType & STICKY_CACHE_ENTRY)
2825 /* Sticky entries have a default exempt time of one day */
2826 pUrlEntry->dwExemptDelta = 86400;
2828 else
2829 pUrlEntry->dwExemptDelta = 0;
2830 pUrlEntry->dwHitRate = 0;
2831 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2832 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2833 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2834 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2835 pUrlEntry->size.QuadPart = file_size.QuadPart;
2836 pUrlEntry->dwUseCount = 0;
2837 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2838 pUrlEntry->LastModifiedTime = LastModifiedTime;
2839 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2840 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2841 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2842 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2844 /*** Unknowns ***/
2845 pUrlEntry->dwUnknown1 = 0;
2846 pUrlEntry->dwUnknown2 = 0;
2847 pUrlEntry->dwUnknown3 = 0x60;
2848 pUrlEntry->Unknown4 = 0;
2849 pUrlEntry->wUnknown5 = 0x1010;
2850 pUrlEntry->dwUnknown7 = 0;
2851 pUrlEntry->dwUnknown8 = 0;
2854 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2855 if (dwOffsetLocalFileName)
2856 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
2857 if (dwOffsetHeader)
2858 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2859 if (dwOffsetFileExtension)
2860 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2862 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
2863 while (error == ERROR_HANDLE_DISK_FULL)
2865 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2866 if (error == ERROR_SUCCESS)
2868 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
2869 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2870 url_entry_offset, HASHTABLE_URL);
2873 if (error != ERROR_SUCCESS)
2874 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2875 else
2877 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2878 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2879 if (CacheEntryType & STICKY_CACHE_ENTRY)
2880 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2881 else
2882 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2883 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2884 pHeader->CacheLimit.QuadPart)
2885 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2888 cleanup:
2889 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2890 heap_free(lpszUrlNameA);
2891 heap_free(lpszFileExtensionA);
2893 if (error == ERROR_SUCCESS)
2894 return TRUE;
2895 else
2897 SetLastError(error);
2898 return FALSE;
2902 /***********************************************************************
2903 * CommitUrlCacheEntryA (WININET.@)
2906 BOOL WINAPI CommitUrlCacheEntryA(
2907 IN LPCSTR lpszUrlName,
2908 IN LPCSTR lpszLocalFileName,
2909 IN FILETIME ExpireTime,
2910 IN FILETIME LastModifiedTime,
2911 IN DWORD CacheEntryType,
2912 IN LPBYTE lpHeaderInfo,
2913 IN DWORD dwHeaderSize,
2914 IN LPCSTR lpszFileExtension,
2915 IN LPCSTR lpszOriginalUrl
2918 WCHAR *url_name = NULL;
2919 WCHAR *local_file_name = NULL;
2920 WCHAR *original_url = NULL;
2921 WCHAR *file_extension = NULL;
2922 BOOL bSuccess = FALSE;
2924 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2925 debugstr_a(lpszUrlName),
2926 debugstr_a(lpszLocalFileName),
2927 CacheEntryType,
2928 lpHeaderInfo,
2929 dwHeaderSize,
2930 debugstr_a(lpszFileExtension),
2931 debugstr_a(lpszOriginalUrl));
2933 url_name = heap_strdupAtoW(lpszUrlName);
2934 if (!url_name)
2935 goto cleanup;
2937 if (lpszLocalFileName)
2939 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2940 if (!local_file_name)
2941 goto cleanup;
2943 if (lpszFileExtension)
2945 file_extension = heap_strdupAtoW(lpszFileExtension);
2946 if (!file_extension)
2947 goto cleanup;
2949 if (lpszOriginalUrl)
2951 original_url = heap_strdupAtoW(lpszOriginalUrl);
2952 if (!original_url)
2953 goto cleanup;
2956 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2957 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2958 file_extension, original_url);
2960 cleanup:
2961 heap_free(original_url);
2962 heap_free(file_extension);
2963 heap_free(local_file_name);
2964 heap_free(url_name);
2965 return bSuccess;
2968 /***********************************************************************
2969 * CommitUrlCacheEntryW (WININET.@)
2972 BOOL WINAPI CommitUrlCacheEntryW(
2973 IN LPCWSTR lpszUrlName,
2974 IN LPCWSTR lpszLocalFileName,
2975 IN FILETIME ExpireTime,
2976 IN FILETIME LastModifiedTime,
2977 IN DWORD CacheEntryType,
2978 IN LPWSTR lpHeaderInfo,
2979 IN DWORD dwHeaderSize,
2980 IN LPCWSTR lpszFileExtension,
2981 IN LPCWSTR lpszOriginalUrl
2984 DWORD dwError = 0;
2985 BOOL bSuccess = FALSE;
2986 DWORD len = 0;
2987 CHAR *header_info = NULL;
2989 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2990 debugstr_w(lpszUrlName),
2991 debugstr_w(lpszLocalFileName),
2992 CacheEntryType,
2993 lpHeaderInfo,
2994 dwHeaderSize,
2995 debugstr_w(lpszFileExtension),
2996 debugstr_w(lpszOriginalUrl));
2998 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3000 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3001 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3003 bSuccess = TRUE;
3005 else
3007 dwError = GetLastError();
3009 if (header_info)
3011 heap_free(header_info);
3012 if (!bSuccess)
3013 SetLastError(dwError);
3016 return bSuccess;
3019 /***********************************************************************
3020 * ReadUrlCacheEntryStream (WININET.@)
3023 BOOL WINAPI ReadUrlCacheEntryStream(
3024 IN HANDLE hUrlCacheStream,
3025 IN DWORD dwLocation,
3026 IN OUT LPVOID lpBuffer,
3027 IN OUT LPDWORD lpdwLen,
3028 IN DWORD dwReserved
3031 /* Get handle to file from 'stream' */
3032 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3034 if (dwReserved != 0)
3036 ERR("dwReserved != 0\n");
3037 SetLastError(ERROR_INVALID_PARAMETER);
3038 return FALSE;
3041 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3043 SetLastError(ERROR_INVALID_HANDLE);
3044 return FALSE;
3047 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3048 return FALSE;
3049 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3052 /***********************************************************************
3053 * RetrieveUrlCacheEntryStreamA (WININET.@)
3056 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3057 IN LPCSTR lpszUrlName,
3058 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3059 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3060 IN BOOL fRandomRead,
3061 IN DWORD dwReserved
3064 /* NOTE: this is not the same as the way that the native
3065 * version allocates 'stream' handles. I did it this way
3066 * as it is much easier and no applications should depend
3067 * on this behaviour. (Native version appears to allocate
3068 * indices into a table)
3070 STREAM_HANDLE * pStream;
3071 HANDLE hFile;
3073 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3074 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3076 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3077 lpCacheEntryInfo,
3078 lpdwCacheEntryInfoBufferSize,
3079 dwReserved))
3081 return NULL;
3084 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3085 GENERIC_READ,
3086 FILE_SHARE_READ,
3087 NULL,
3088 OPEN_EXISTING,
3089 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3090 NULL);
3091 if (hFile == INVALID_HANDLE_VALUE)
3092 return FALSE;
3094 /* allocate handle storage space */
3095 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3096 if (!pStream)
3098 CloseHandle(hFile);
3099 SetLastError(ERROR_OUTOFMEMORY);
3100 return FALSE;
3103 pStream->hFile = hFile;
3104 strcpy(pStream->lpszUrl, lpszUrlName);
3105 return pStream;
3108 /***********************************************************************
3109 * RetrieveUrlCacheEntryStreamW (WININET.@)
3112 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3113 IN LPCWSTR lpszUrlName,
3114 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3115 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3116 IN BOOL fRandomRead,
3117 IN DWORD dwReserved
3120 DWORD size;
3121 int url_len;
3122 /* NOTE: this is not the same as the way that the native
3123 * version allocates 'stream' handles. I did it this way
3124 * as it is much easier and no applications should depend
3125 * on this behaviour. (Native version appears to allocate
3126 * indices into a table)
3128 STREAM_HANDLE * pStream;
3129 HANDLE hFile;
3131 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3132 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3134 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3135 lpCacheEntryInfo,
3136 lpdwCacheEntryInfoBufferSize,
3137 dwReserved))
3139 return NULL;
3142 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3143 GENERIC_READ,
3144 FILE_SHARE_READ,
3145 NULL,
3146 OPEN_EXISTING,
3147 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3148 NULL);
3149 if (hFile == INVALID_HANDLE_VALUE)
3150 return FALSE;
3152 /* allocate handle storage space */
3153 size = sizeof(STREAM_HANDLE);
3154 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3155 size += url_len;
3156 pStream = heap_alloc(size);
3157 if (!pStream)
3159 CloseHandle(hFile);
3160 SetLastError(ERROR_OUTOFMEMORY);
3161 return FALSE;
3164 pStream->hFile = hFile;
3165 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3166 return pStream;
3169 /***********************************************************************
3170 * UnlockUrlCacheEntryStream (WININET.@)
3173 BOOL WINAPI UnlockUrlCacheEntryStream(
3174 IN HANDLE hUrlCacheStream,
3175 IN DWORD dwReserved
3178 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3180 if (dwReserved != 0)
3182 ERR("dwReserved != 0\n");
3183 SetLastError(ERROR_INVALID_PARAMETER);
3184 return FALSE;
3187 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3189 SetLastError(ERROR_INVALID_HANDLE);
3190 return FALSE;
3193 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3194 return FALSE;
3196 CloseHandle(pStream->hFile);
3197 heap_free(pStream);
3198 return TRUE;
3202 /***********************************************************************
3203 * DeleteUrlCacheEntryA (WININET.@)
3206 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3208 URLCACHECONTAINER * pContainer;
3209 LPURLCACHE_HEADER pHeader;
3210 struct _HASH_ENTRY * pHashEntry;
3211 DWORD error;
3212 BOOL ret;
3214 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3216 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3217 if (error != ERROR_SUCCESS)
3219 SetLastError(error);
3220 return FALSE;
3223 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3224 if (error != ERROR_SUCCESS)
3226 SetLastError(error);
3227 return FALSE;
3230 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3231 return FALSE;
3233 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3235 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3236 TRACE("entry %s not found!\n", lpszUrlName);
3237 SetLastError(ERROR_FILE_NOT_FOUND);
3238 return FALSE;
3241 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3243 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3245 return ret;
3248 /***********************************************************************
3249 * DeleteUrlCacheEntryW (WININET.@)
3252 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3254 URLCACHECONTAINER * pContainer;
3255 LPURLCACHE_HEADER pHeader;
3256 struct _HASH_ENTRY * pHashEntry;
3257 LPSTR urlA;
3258 DWORD error;
3259 BOOL ret;
3261 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3263 urlA = heap_strdupWtoA(lpszUrlName);
3264 if (!urlA)
3266 SetLastError(ERROR_OUTOFMEMORY);
3267 return FALSE;
3270 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3271 if (error != ERROR_SUCCESS)
3273 heap_free(urlA);
3274 SetLastError(error);
3275 return FALSE;
3278 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3279 if (error != ERROR_SUCCESS)
3281 heap_free(urlA);
3282 SetLastError(error);
3283 return FALSE;
3286 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3288 heap_free(urlA);
3289 return FALSE;
3292 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3294 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3295 TRACE("entry %s not found!\n", debugstr_a(urlA));
3296 heap_free(urlA);
3297 SetLastError(ERROR_FILE_NOT_FOUND);
3298 return FALSE;
3301 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3303 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3304 heap_free(urlA);
3305 return ret;
3308 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3310 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3311 return TRUE;
3314 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3316 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3317 return TRUE;
3320 /***********************************************************************
3321 * CreateCacheContainerA (WININET.@)
3323 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3324 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3326 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3327 d1, d2, d3, d4, d5, d6, d7, d8);
3328 return TRUE;
3331 /***********************************************************************
3332 * CreateCacheContainerW (WININET.@)
3334 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3335 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3337 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3338 d1, d2, d3, d4, d5, d6, d7, d8);
3339 return TRUE;
3342 /***********************************************************************
3343 * FindFirstUrlCacheContainerA (WININET.@)
3345 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3347 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3348 return NULL;
3351 /***********************************************************************
3352 * FindFirstUrlCacheContainerW (WININET.@)
3354 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3356 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3357 return NULL;
3360 /***********************************************************************
3361 * FindNextUrlCacheContainerA (WININET.@)
3363 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3365 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3366 return FALSE;
3369 /***********************************************************************
3370 * FindNextUrlCacheContainerW (WININET.@)
3372 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3374 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3375 return FALSE;
3378 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3379 LPCSTR lpszUrlSearchPattern,
3380 DWORD dwFlags,
3381 DWORD dwFilter,
3382 GROUPID GroupId,
3383 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3384 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3385 LPVOID lpReserved,
3386 LPDWORD pcbReserved2,
3387 LPVOID lpReserved3
3390 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3391 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3392 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3393 SetLastError(ERROR_FILE_NOT_FOUND);
3394 return NULL;
3397 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3398 LPCWSTR lpszUrlSearchPattern,
3399 DWORD dwFlags,
3400 DWORD dwFilter,
3401 GROUPID GroupId,
3402 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3403 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3404 LPVOID lpReserved,
3405 LPDWORD pcbReserved2,
3406 LPVOID lpReserved3
3409 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3410 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3411 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3412 SetLastError(ERROR_FILE_NOT_FOUND);
3413 return NULL;
3416 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3418 typedef struct URLCacheFindEntryHandle
3420 DWORD dwMagic;
3421 LPWSTR lpszUrlSearchPattern;
3422 DWORD dwContainerIndex;
3423 DWORD dwHashTableIndex;
3424 DWORD dwHashEntryIndex;
3425 } URLCacheFindEntryHandle;
3427 /***********************************************************************
3428 * FindFirstUrlCacheEntryA (WININET.@)
3431 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3432 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3434 URLCacheFindEntryHandle *pEntryHandle;
3436 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3438 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3439 if (!pEntryHandle)
3440 return NULL;
3442 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3443 if (lpszUrlSearchPattern)
3445 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3446 if (!pEntryHandle->lpszUrlSearchPattern)
3448 heap_free(pEntryHandle);
3449 return NULL;
3452 else
3453 pEntryHandle->lpszUrlSearchPattern = NULL;
3454 pEntryHandle->dwContainerIndex = 0;
3455 pEntryHandle->dwHashTableIndex = 0;
3456 pEntryHandle->dwHashEntryIndex = 0;
3458 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3460 heap_free(pEntryHandle);
3461 return NULL;
3463 return pEntryHandle;
3466 /***********************************************************************
3467 * FindFirstUrlCacheEntryW (WININET.@)
3470 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3471 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3473 URLCacheFindEntryHandle *pEntryHandle;
3475 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3477 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3478 if (!pEntryHandle)
3479 return NULL;
3481 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3482 if (lpszUrlSearchPattern)
3484 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3485 if (!pEntryHandle->lpszUrlSearchPattern)
3487 heap_free(pEntryHandle);
3488 return NULL;
3491 else
3492 pEntryHandle->lpszUrlSearchPattern = NULL;
3493 pEntryHandle->dwContainerIndex = 0;
3494 pEntryHandle->dwHashTableIndex = 0;
3495 pEntryHandle->dwHashEntryIndex = 0;
3497 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3499 heap_free(pEntryHandle);
3500 return NULL;
3502 return pEntryHandle;
3505 static BOOL FindNextUrlCacheEntryInternal(
3506 HANDLE hEnumHandle,
3507 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3508 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3509 BOOL unicode)
3511 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3512 URLCACHECONTAINER * pContainer;
3514 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3516 SetLastError(ERROR_INVALID_HANDLE);
3517 return FALSE;
3520 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3521 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3523 LPURLCACHE_HEADER pHeader;
3524 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3525 DWORD error;
3527 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3528 if (error != ERROR_SUCCESS)
3530 SetLastError(error);
3531 return FALSE;
3534 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3535 return FALSE;
3537 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3538 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3540 const struct _HASH_ENTRY *pHashEntry = NULL;
3541 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3542 pEntryHandle->dwHashEntryIndex++)
3544 const URL_CACHEFILE_ENTRY *pUrlEntry;
3545 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3547 if (pEntry->dwSignature != URL_SIGNATURE)
3548 continue;
3550 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3551 TRACE("Found URL: %s\n",
3552 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3553 TRACE("Header info: %s\n",
3554 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3556 error = URLCache_CopyEntry(
3557 pContainer,
3558 pHeader,
3559 lpNextCacheEntryInfo,
3560 lpdwNextCacheEntryInfoBufferSize,
3561 pUrlEntry,
3562 unicode);
3563 if (error != ERROR_SUCCESS)
3565 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3566 SetLastError(error);
3567 return FALSE;
3569 TRACE("Local File Name: %s\n",
3570 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3572 /* increment the current index so that next time the function
3573 * is called the next entry is returned */
3574 pEntryHandle->dwHashEntryIndex++;
3575 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3576 return TRUE;
3580 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3583 SetLastError(ERROR_NO_MORE_ITEMS);
3584 return FALSE;
3587 /***********************************************************************
3588 * FindNextUrlCacheEntryA (WININET.@)
3590 BOOL WINAPI FindNextUrlCacheEntryA(
3591 HANDLE hEnumHandle,
3592 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3593 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3595 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3597 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3598 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3601 /***********************************************************************
3602 * FindNextUrlCacheEntryW (WININET.@)
3604 BOOL WINAPI FindNextUrlCacheEntryW(
3605 HANDLE hEnumHandle,
3606 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3607 LPDWORD lpdwNextCacheEntryInfoBufferSize
3610 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3612 return FindNextUrlCacheEntryInternal(hEnumHandle,
3613 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3614 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3617 /***********************************************************************
3618 * FindCloseUrlCache (WININET.@)
3620 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3622 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3624 TRACE("(%p)\n", hEnumHandle);
3626 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3628 SetLastError(ERROR_INVALID_HANDLE);
3629 return FALSE;
3632 pEntryHandle->dwMagic = 0;
3633 heap_free(pEntryHandle->lpszUrlSearchPattern);
3634 heap_free(pEntryHandle);
3635 return TRUE;
3638 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3639 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3641 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3642 dwSearchCondition, lpGroupId, lpReserved);
3643 return NULL;
3646 BOOL WINAPI FindNextUrlCacheEntryExA(
3647 HANDLE hEnumHandle,
3648 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3649 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3650 LPVOID lpReserved,
3651 LPDWORD pcbReserved2,
3652 LPVOID lpReserved3
3655 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3656 lpReserved, pcbReserved2, lpReserved3);
3657 return FALSE;
3660 BOOL WINAPI FindNextUrlCacheEntryExW(
3661 HANDLE hEnumHandle,
3662 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3663 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3664 LPVOID lpReserved,
3665 LPDWORD pcbReserved2,
3666 LPVOID lpReserved3
3669 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3670 lpReserved, pcbReserved2, lpReserved3);
3671 return FALSE;
3674 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3676 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3677 return FALSE;
3680 /***********************************************************************
3681 * CreateUrlCacheGroup (WININET.@)
3684 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3686 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3687 return FALSE;
3690 /***********************************************************************
3691 * DeleteUrlCacheGroup (WININET.@)
3694 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3696 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3697 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3698 return FALSE;
3701 /***********************************************************************
3702 * SetUrlCacheEntryGroupA (WININET.@)
3705 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3706 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3707 LPVOID lpReserved)
3709 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3710 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3711 pbGroupAttributes, cbGroupAttributes, lpReserved);
3712 SetLastError(ERROR_FILE_NOT_FOUND);
3713 return FALSE;
3716 /***********************************************************************
3717 * SetUrlCacheEntryGroupW (WININET.@)
3720 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3721 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3722 LPVOID lpReserved)
3724 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3725 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3726 pbGroupAttributes, cbGroupAttributes, lpReserved);
3727 SetLastError(ERROR_FILE_NOT_FOUND);
3728 return FALSE;
3731 /***********************************************************************
3732 * GetUrlCacheConfigInfoW (WININET.@)
3734 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3736 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3737 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3738 return FALSE;
3741 /***********************************************************************
3742 * GetUrlCacheConfigInfoA (WININET.@)
3744 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3746 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3747 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3748 return FALSE;
3751 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3752 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3753 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3755 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3756 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3757 lpdwGroupInfo, lpReserved);
3758 return FALSE;
3761 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3762 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3763 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3765 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3766 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3767 lpdwGroupInfo, lpReserved);
3768 return FALSE;
3771 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3772 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3774 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3775 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3776 return TRUE;
3779 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3780 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3782 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3783 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3784 return TRUE;
3787 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3789 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3790 return TRUE;
3793 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3795 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3796 return TRUE;
3799 /***********************************************************************
3800 * DeleteIE3Cache (WININET.@)
3802 * Deletes the files used by the IE3 URL caching system.
3804 * PARAMS
3805 * hWnd [I] A dummy window.
3806 * hInst [I] Instance of process calling the function.
3807 * lpszCmdLine [I] Options used by function.
3808 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3810 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3812 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3813 return 0;
3816 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3817 FILETIME *pftLastModified)
3819 BOOL ret;
3820 FILETIME now, expired;
3822 *pftLastModified = pUrlEntry->LastModifiedTime;
3823 GetSystemTimeAsFileTime(&now);
3824 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3825 pUrlEntry->wExpiredTime, &expired);
3826 /* If the expired time is 0, it's interpreted as not expired */
3827 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3828 ret = FALSE;
3829 else
3830 ret = CompareFileTime(&expired, &now) < 0;
3831 return ret;
3834 /***********************************************************************
3835 * IsUrlCacheEntryExpiredA (WININET.@)
3837 * PARAMS
3838 * url [I] Url
3839 * dwFlags [I] Unknown
3840 * pftLastModified [O] Last modified time
3842 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3844 LPURLCACHE_HEADER pHeader;
3845 struct _HASH_ENTRY * pHashEntry;
3846 const CACHEFILE_ENTRY * pEntry;
3847 const URL_CACHEFILE_ENTRY * pUrlEntry;
3848 URLCACHECONTAINER * pContainer;
3849 BOOL expired;
3851 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3853 if (!url || !pftLastModified)
3854 return TRUE;
3855 if (dwFlags)
3856 FIXME("unknown flags 0x%08x\n", dwFlags);
3858 /* Any error implies that the URL is expired, i.e. not in the cache */
3859 if (URLCacheContainers_FindContainerA(url, &pContainer))
3861 memset(pftLastModified, 0, sizeof(*pftLastModified));
3862 return TRUE;
3865 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3867 memset(pftLastModified, 0, sizeof(*pftLastModified));
3868 return TRUE;
3871 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3873 memset(pftLastModified, 0, sizeof(*pftLastModified));
3874 return TRUE;
3877 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3879 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3880 memset(pftLastModified, 0, sizeof(*pftLastModified));
3881 TRACE("entry %s not found!\n", url);
3882 return TRUE;
3885 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3886 if (pEntry->dwSignature != URL_SIGNATURE)
3888 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3889 memset(pftLastModified, 0, sizeof(*pftLastModified));
3890 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3891 return TRUE;
3894 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3895 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3897 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3899 return expired;
3902 /***********************************************************************
3903 * IsUrlCacheEntryExpiredW (WININET.@)
3905 * PARAMS
3906 * url [I] Url
3907 * dwFlags [I] Unknown
3908 * pftLastModified [O] Last modified time
3910 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3912 LPURLCACHE_HEADER pHeader;
3913 struct _HASH_ENTRY * pHashEntry;
3914 const CACHEFILE_ENTRY * pEntry;
3915 const URL_CACHEFILE_ENTRY * pUrlEntry;
3916 URLCACHECONTAINER * pContainer;
3917 BOOL expired;
3919 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3921 if (!url || !pftLastModified)
3922 return TRUE;
3923 if (dwFlags)
3924 FIXME("unknown flags 0x%08x\n", dwFlags);
3926 /* Any error implies that the URL is expired, i.e. not in the cache */
3927 if (URLCacheContainers_FindContainerW(url, &pContainer))
3929 memset(pftLastModified, 0, sizeof(*pftLastModified));
3930 return TRUE;
3933 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3935 memset(pftLastModified, 0, sizeof(*pftLastModified));
3936 return TRUE;
3939 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3941 memset(pftLastModified, 0, sizeof(*pftLastModified));
3942 return TRUE;
3945 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3947 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3948 memset(pftLastModified, 0, sizeof(*pftLastModified));
3949 TRACE("entry %s not found!\n", debugstr_w(url));
3950 return TRUE;
3953 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3955 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3956 memset(pftLastModified, 0, sizeof(*pftLastModified));
3957 TRACE("entry %s not found!\n", debugstr_w(url));
3958 return TRUE;
3961 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3962 if (pEntry->dwSignature != URL_SIGNATURE)
3964 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3965 memset(pftLastModified, 0, sizeof(*pftLastModified));
3966 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3967 return TRUE;
3970 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3971 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3975 return expired;
3978 /***********************************************************************
3979 * GetDiskInfoA (WININET.@)
3981 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3983 BOOL ret;
3984 ULARGE_INTEGER bytes_free, bytes_total;
3986 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3988 if (!path)
3990 SetLastError(ERROR_INVALID_PARAMETER);
3991 return FALSE;
3994 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3996 if (cluster_size) *cluster_size = 1;
3997 if (free) *free = bytes_free.QuadPart;
3998 if (total) *total = bytes_total.QuadPart;
4000 return ret;
4003 /***********************************************************************
4004 * RegisterUrlCacheNotification (WININET.@)
4006 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4008 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4009 return 0;
4012 /***********************************************************************
4013 * IncrementUrlCacheHeaderData (WININET.@)
4015 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4017 FIXME("(%u, %p)\n", index, data);
4018 return FALSE;
4021 /***********************************************************************
4022 * RunOnceUrlCache (WININET.@)
4025 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4027 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4028 return 0;