Fixed mem leak on error path.
[wine/multimedia.git] / dlls / wininet / urlcache.c
blobd77b9379314b5b63d815d97529d759eb2e588903
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #define COM_NO_WINDOWS_H
26 #include "config.h"
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "wininet.h"
38 #include "winerror.h"
39 #include "internet.h"
40 #include "winreg.h"
41 #include "shlwapi.h"
42 #include "wingdi.h"
43 #include "shlobj.h"
45 #include "wine/unicode.h"
46 #include "wine/list.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
51 #define ENTRY_START_OFFSET 0x4000
52 #define DIR_LENGTH 8
53 #define BLOCKSIZE 128
54 #define HASHTABLE_SIZE 448
55 #define HASHTABLE_BLOCKSIZE 7
56 #define HASHTABLE_FREE 3
57 #define ALLOCATION_TABLE_OFFSET 0x250
58 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
59 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
61 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
62 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
63 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
64 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
65 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
67 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
69 typedef struct _CACHEFILE_ENTRY
71 /* union
72 {*/
73 DWORD dwSignature; /* e.g. "URL " */
74 /* CHAR szSignature[4];
75 };*/
76 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
77 } CACHEFILE_ENTRY;
79 typedef struct _URL_CACHEFILE_ENTRY
81 CACHEFILE_ENTRY CacheFileEntry;
82 FILETIME LastModifiedTime;
83 FILETIME LastAccessTime;
84 WORD wExpiredDate; /* expire date in dos format */
85 WORD wExpiredTime; /* expire time in dos format */
86 DWORD dwUnknown1; /* usually zero */
87 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
88 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
89 DWORD dwUnknown2; /* usually zero */
90 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
91 DWORD dwUnknown3; /* usually 0x60 */
92 DWORD dwOffsetUrl; /* usually 0x68 */
93 BYTE CacheDir; /* index of cache directory this url is stored in */
94 BYTE Unknown4; /* usually zero */
95 WORD wUnknown5; /* usually 0x1010 */
96 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
97 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
98 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
99 DWORD dwHeaderInfoSize;
100 DWORD dwUnknown6; /* usually zero */
101 WORD wLastSyncDate; /* last sync date in dos format */
102 WORD wLastSyncTime; /* last sync time in dos format */
103 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
104 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
105 WORD wUnknownDate; /* usually same as wLastSyncDate */
106 WORD wUnknownTime; /* usually same as wLastSyncTime */
107 DWORD dwUnknown7; /* usually zero */
108 DWORD dwUnknown8; /* usually zero */
109 CHAR szSourceUrlName[1]; /* start of url */
110 /* packing to dword align start of next field */
111 /* CHAR szLocalFileName[]; (local file name exluding path) */
112 /* packing to dword align start of next field */
113 /* CHAR szHeaderInfo[]; (header info) */
114 } URL_CACHEFILE_ENTRY;
116 struct _HASH_ENTRY
118 DWORD dwHashKey;
119 DWORD dwOffsetEntry;
122 typedef struct _HASH_CACHEFILE_ENTRY
124 CACHEFILE_ENTRY CacheFileEntry;
125 DWORD dwAddressNext;
126 DWORD dwHashTableNumber;
127 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
128 } HASH_CACHEFILE_ENTRY;
130 typedef struct _DIRECTORY_DATA
132 DWORD dwUnknown;
133 char filename[DIR_LENGTH];
134 } DIRECTORY_DATA;
136 typedef struct _URLCACHE_HEADER
138 char szSignature[28];
139 DWORD dwFileSize;
140 DWORD dwOffsetFirstHashTable;
141 DWORD dwIndexCapacityInBlocks;
142 DWORD dwBlocksInUse; /* is this right? */
143 DWORD dwUnknown1;
144 DWORD dwCacheLimitLow; /* disk space limit for cache */
145 DWORD dwCacheLimitHigh; /* disk space limit for cache */
146 DWORD dwUnknown4; /* current disk space usage for cache? */
147 DWORD dwUnknown5; /* current disk space usage for cache? */
148 DWORD dwUnknown6; /* possibly a flag? */
149 DWORD dwUnknown7;
150 BYTE DirectoryCount; /* number of directory_data's */
151 BYTE Unknown8[3]; /* just padding? */
152 DIRECTORY_DATA directory_data[1]; /* first directory entry */
153 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
154 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
156 typedef struct _STREAM_HANDLE
158 HANDLE hFile;
159 CHAR lpszUrl[1];
160 } STREAM_HANDLE;
162 typedef struct _URLCACHECONTAINER
164 struct list entry; /* part of a list */
165 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
166 LPWSTR path; /* path to url container directory */
167 HANDLE hMapping; /* handle of file mapping */
168 DWORD file_size; /* size of file when mapping was opened */
169 HANDLE hMutex; /* hande of mutex */
170 } URLCACHECONTAINER;
173 /* List of all containers available */
174 static struct list UrlContainers = LIST_INIT(UrlContainers);
177 /***********************************************************************
178 * URLCache_PathToObjectName (Internal)
180 * Converts a path to a name suitable for use as a Win32 object name.
181 * Replaces '\\' characters in-place with the specified character
182 * (usually '_' or '!')
184 * RETURNS
185 * nothing
188 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
190 for (; *lpszPath; lpszPath++)
192 if (*lpszPath == '\\')
193 *lpszPath = replace;
197 /***********************************************************************
198 * URLCacheContainer_OpenIndex (Internal)
200 * Opens the index file and saves mapping handle in hCacheIndexMapping
202 * RETURNS
203 * TRUE if succeeded
204 * FALSE if failed
207 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
209 HANDLE hFile;
210 WCHAR wszFilePath[MAX_PATH];
211 DWORD dwFileSize;
213 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
214 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
216 if (pContainer->hMapping)
217 return TRUE;
219 strcpyW(wszFilePath, pContainer->path);
220 strcatW(wszFilePath, wszIndex);
222 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
223 if (hFile == INVALID_HANDLE_VALUE)
225 FIXME("need to create cache index file\n");
226 return FALSE;
229 dwFileSize = GetFileSize(hFile, NULL);
230 if (dwFileSize == INVALID_FILE_SIZE)
231 return FALSE;
233 if (dwFileSize == 0)
235 FIXME("need to create cache index file\n");
236 return FALSE;
239 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
240 URLCache_PathToObjectName(wszFilePath, '_');
241 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
242 if (!pContainer->hMapping)
243 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
244 CloseHandle(hFile);
245 if (!pContainer->hMapping)
247 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
248 return FALSE;
251 return TRUE;
254 /***********************************************************************
255 * URLCacheContainer_CloseIndex (Internal)
257 * Closes the index
259 * RETURNS
260 * nothing
263 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
265 CloseHandle(pContainer->hMapping);
266 pContainer->hMapping = NULL;
269 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
271 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
272 int path_len = strlenW(path);
273 int cache_prefix_len = strlenW(cache_prefix);
275 if (!pContainer)
277 return FALSE;
280 pContainer->hMapping = NULL;
281 pContainer->file_size = 0;
283 pContainer->path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
284 if (!pContainer->path)
286 HeapFree(GetProcessHeap(), 0, pContainer);
287 return FALSE;
290 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
292 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
293 if (!pContainer->cache_prefix)
295 HeapFree(GetProcessHeap(), 0, pContainer->path);
296 HeapFree(GetProcessHeap(), 0, pContainer);
297 return FALSE;
300 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
302 CharLowerW(mutex_name);
303 URLCache_PathToObjectName(mutex_name, '!');
305 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
307 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
308 HeapFree(GetProcessHeap(), 0, pContainer->path);
309 HeapFree(GetProcessHeap(), 0, pContainer);
310 return FALSE;
313 list_add_head(&UrlContainers, &pContainer->entry);
315 return TRUE;
318 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
320 list_remove(&pContainer->entry);
322 URLCacheContainer_CloseIndex(pContainer);
323 CloseHandle(pContainer->hMutex);
324 HeapFree(GetProcessHeap(), 0, pContainer->path);
325 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
326 HeapFree(GetProcessHeap(), 0, pContainer);
329 void URLCacheContainers_CreateDefaults()
331 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
332 static const WCHAR UrlPrefix[] = {0};
333 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
334 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
335 static const WCHAR CookieSuffix[] = {0};
336 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
337 static const struct
339 int nFolder; /* CSIDL_* constant */
340 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
341 const WCHAR * cache_prefix; /* prefix used to reference the container */
342 } DefaultContainerData[] =
344 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
345 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
346 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
348 int i;
350 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
352 WCHAR wszCachePath[MAX_PATH];
353 WCHAR wszMutexName[MAX_PATH];
354 int path_len, suffix_len;
356 if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
358 ERR("Couldn't get path for default container %d\n", i);
359 continue;
361 path_len = strlenW(wszCachePath);
362 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
364 if (path_len + suffix_len + 2 > MAX_PATH)
366 ERR("Path too long\n");
367 continue;
370 wszCachePath[path_len] = '\\';
372 strcpyW(wszMutexName, wszCachePath);
374 if (suffix_len)
376 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
377 wszCachePath[path_len + suffix_len + 1] = '\\';
378 wszCachePath[path_len + suffix_len + 2] = '\0';
381 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
385 void URLCacheContainers_DeleteAll()
387 while(!list_empty(&UrlContainers))
388 URLCacheContainer_DeleteContainer(
389 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
393 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
395 struct list * cursor;
397 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
399 LIST_FOR_EACH(cursor, &UrlContainers)
401 URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
402 int prefix_len = strlenW(pContainer->cache_prefix);
403 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
405 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
406 *ppContainer = pContainer;
407 return TRUE;
410 ERR("no container found\n");
411 SetLastError(ERROR_FILE_NOT_FOUND);
412 return FALSE;
415 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
417 BOOL ret;
418 LPWSTR lpwszUrl;
419 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
420 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
422 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
423 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
424 HeapFree(GetProcessHeap(), 0, lpwszUrl);
425 return ret;
427 return FALSE;
430 /***********************************************************************
431 * URLCacheContainer_LockIndex (Internal)
434 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
436 BYTE index;
437 LPVOID pIndexData;
438 URLCACHE_HEADER * pHeader;
440 /* acquire mutex */
441 WaitForSingleObject(pContainer->hMutex, INFINITE);
443 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
445 if (!pIndexData)
447 ReleaseMutex(pContainer->hMutex);
448 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
449 return FALSE;
451 pHeader = (URLCACHE_HEADER *)pIndexData;
453 /* file has grown - we need to remap to prevent us getting
454 * access violations when we try and access beyond the end
455 * of the memory mapped file */
456 if (pHeader->dwFileSize != pContainer->file_size)
458 URLCacheContainer_CloseIndex(pContainer);
459 if (!URLCacheContainer_OpenIndex(pContainer))
461 ReleaseMutex(pContainer->hMutex);
462 return FALSE;
464 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
466 if (!pIndexData)
468 ReleaseMutex(pContainer->hMutex);
469 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
470 return FALSE;
472 pHeader = (URLCACHE_HEADER *)pIndexData;
475 TRACE("Signature: %s, file size: %ld bytes\n", pHeader->szSignature, pHeader->dwFileSize);
477 for (index = 0; index < pHeader->DirectoryCount; index++)
479 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
482 return pHeader;
485 /***********************************************************************
486 * URLCacheContainer_UnlockIndex (Internal)
489 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
491 /* release mutex */
492 ReleaseMutex(pContainer->hMutex);
493 return UnmapViewOfFile(pHeader);
497 #ifndef CHAR_BIT
498 #define CHAR_BIT (8 * sizeof(CHAR))
499 #endif
501 /***********************************************************************
502 * URLCache_Allocation_BlockIsFree (Internal)
504 * Is the specified block number free?
506 * RETURNS
507 * zero if free
508 * non-zero otherwise
511 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
513 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
514 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
517 /***********************************************************************
518 * URLCache_Allocation_BlockFree (Internal)
520 * Marks the specified block as free
522 * RETURNS
523 * nothing
526 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
528 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
529 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
532 /***********************************************************************
533 * URLCache_Allocation_BlockAllocate (Internal)
535 * Marks the specified block as allocated
537 * RETURNS
538 * nothing
541 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
543 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
544 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
547 /***********************************************************************
548 * URLCache_FindEntry (Internal)
550 * Finds an entry without using the hash tables
552 * RETURNS
553 * TRUE if it found the specified entry
554 * FALSE otherwise
557 static BOOL URLCache_FindEntry(LPCURLCACHE_HEADER pHeader, LPCSTR szUrl, CACHEFILE_ENTRY ** ppEntry)
559 CACHEFILE_ENTRY * pCurrentEntry;
560 DWORD dwBlockNumber;
562 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
564 for (pCurrentEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET);
565 (DWORD)((LPBYTE)pCurrentEntry - (LPBYTE)pHeader) < pHeader->dwFileSize;
566 pCurrentEntry = (CACHEFILE_ENTRY *)((LPBYTE)pCurrentEntry + pCurrentEntry->dwBlocksUsed * BLOCKSIZE))
568 dwBlockNumber = (DWORD)((LPBYTE)pCurrentEntry - (LPBYTE)pHeader - ENTRY_START_OFFSET) / BLOCKSIZE;
569 while (URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber))
571 if (dwBlockNumber >= pHeader->dwIndexCapacityInBlocks)
572 return FALSE;
574 pCurrentEntry = (CACHEFILE_ENTRY *)((LPBYTE)pCurrentEntry + BLOCKSIZE);
575 dwBlockNumber = (DWORD)((LPBYTE)pCurrentEntry - (LPBYTE)pHeader - ENTRY_START_OFFSET) / BLOCKSIZE;
578 switch (pCurrentEntry->dwSignature)
580 case URL_SIGNATURE: /* "URL " */
581 case LEAK_SIGNATURE: /* "LEAK" */
583 URL_CACHEFILE_ENTRY * pUrlEntry = (URL_CACHEFILE_ENTRY *)pCurrentEntry;
584 if (!strcmp(szUrl, pUrlEntry->szSourceUrlName))
586 *ppEntry = pCurrentEntry;
587 /* FIXME: should we update the LastAccessTime here? */
588 return TRUE;
591 break;
592 case HASH_SIGNATURE: /* HASH entries parsed in FindEntryInHash */
593 case 0xDEADBEEF: /* this is always at offset 0x4000 in URL cache for some reason */
594 break;
595 default:
596 FIXME("Unknown entry %.4s ignored\n", (LPCSTR)&pCurrentEntry->dwSignature);
600 return FALSE;
603 /***********************************************************************
604 * URLCache_FindFirstFreeEntry (Internal)
606 * Finds and allocates the first block of free space big enough and
607 * sets ppEntry to point to it.
609 * RETURNS
610 * TRUE if it had enough space
611 * FALSE if it couldn't find enough space
614 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
616 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
617 DWORD dwBlockNumber;
618 DWORD dwFreeCounter;
619 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
621 for (dwFreeCounter = 0;
622 dwFreeCounter < dwBlocksNeeded &&
623 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
624 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
625 dwFreeCounter++)
626 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
628 if (dwFreeCounter == dwBlocksNeeded)
630 DWORD index;
631 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
632 for (index = 0; index < dwBlocksNeeded; index++)
633 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
634 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
635 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
636 return TRUE;
639 FIXME("Grow file\n");
640 return FALSE;
643 /***********************************************************************
644 * URLCache_DeleteEntry (Internal)
646 * Deletes the specified entry and frees the space allocated to it
648 * RETURNS
649 * TRUE if it succeeded
650 * FALSE if it failed
653 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
655 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
656 return TRUE;
659 /***********************************************************************
660 * URLCache_LocalFileNameToPathW (Internal)
662 * Copies the full path to the specified buffer given the local file
663 * name and the index of the directory it is in. Always sets value in
664 * lpBufferSize to the required buffer size (in bytes).
666 * RETURNS
667 * TRUE if the buffer was big enough
668 * FALSE if the buffer was too small
671 static BOOL URLCache_LocalFileNameToPathW(
672 const URLCACHECONTAINER * pContainer,
673 LPCURLCACHE_HEADER pHeader,
674 LPCSTR szLocalFileName,
675 BYTE Directory,
676 LPWSTR wszPath,
677 LPLONG lpBufferSize)
679 LONG nRequired;
680 int path_len = strlenW(pContainer->path);
681 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
682 if (Directory >= pHeader->DirectoryCount)
684 *lpBufferSize = 0;
685 return FALSE;
688 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
689 if (nRequired < *lpBufferSize)
691 int dir_len;
693 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
694 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
695 wszPath[dir_len + path_len] = '\\';
696 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
697 *lpBufferSize = nRequired;
698 return TRUE;
700 *lpBufferSize = nRequired;
701 return FALSE;
704 /***********************************************************************
705 * URLCache_LocalFileNameToPathA (Internal)
707 * Copies the full path to the specified buffer given the local file
708 * name and the index of the directory it is in. Always sets value in
709 * lpBufferSize to the required buffer size.
711 * RETURNS
712 * TRUE if the buffer was big enough
713 * FALSE if the buffer was too small
716 static BOOL URLCache_LocalFileNameToPathA(
717 const URLCACHECONTAINER * pContainer,
718 LPCURLCACHE_HEADER pHeader,
719 LPCSTR szLocalFileName,
720 BYTE Directory,
721 LPSTR szPath,
722 LPLONG lpBufferSize)
724 LONG nRequired;
725 int path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
726 int file_name_len = strlen(szLocalFileName);
727 int dir_len = DIR_LENGTH;
728 if (Directory >= pHeader->DirectoryCount)
730 *lpBufferSize = 0;
731 return FALSE;
734 nRequired = (path_len + dir_len + file_name_len + 1) * sizeof(WCHAR);
735 if (nRequired < *lpBufferSize)
737 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
738 strncpy(szPath, pHeader->directory_data[Directory].filename, DIR_LENGTH);
739 szPath[dir_len + path_len] = '\\';
740 strcpy(szPath + dir_len + path_len + 1, szLocalFileName);
741 *lpBufferSize = nRequired;
742 return TRUE;
744 *lpBufferSize = nRequired;
745 return FALSE;
748 /***********************************************************************
749 * URLCache_CopyEntry (Internal)
751 * Copies an entry from the cache index file to the Win32 structure
753 * RETURNS
754 * TRUE if the buffer was big enough
755 * FALSE if the buffer was too small
758 static BOOL URLCache_CopyEntry(
759 URLCACHECONTAINER * pContainer,
760 LPCURLCACHE_HEADER pHeader,
761 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
762 LPDWORD lpdwBufferSize,
763 URL_CACHEFILE_ENTRY * pUrlEntry,
764 BOOL bUnicode)
766 int lenUrl;
767 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
769 if (*lpdwBufferSize >= dwRequiredSize)
771 lpCacheEntryInfo->lpHeaderInfo = NULL;
772 lpCacheEntryInfo->lpszFileExtension = NULL;
773 lpCacheEntryInfo->lpszLocalFileName = NULL;
774 lpCacheEntryInfo->lpszSourceUrlName = NULL;
775 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
776 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
777 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
778 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
779 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
780 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
781 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
782 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
783 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
784 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
785 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
786 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
787 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
788 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
791 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
792 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
793 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
794 if (bUnicode)
795 lenUrl = MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, NULL, 0);
796 else
797 lenUrl = strlen(pUrlEntry->szSourceUrlName);
798 dwRequiredSize += lenUrl + 1;
800 /* FIXME: is source url optional? */
801 if (*lpdwBufferSize >= dwRequiredSize)
803 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
804 if (bUnicode)
805 MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
806 else
807 memcpy(lpCacheEntryInfo->lpszSourceUrlName, pUrlEntry->szSourceUrlName, (lenUrl + 1) * sizeof(CHAR));
810 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
811 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
812 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
814 if (pUrlEntry->dwOffsetLocalName)
816 LONG nLocalFilePathSize;
817 LPSTR lpszLocalFileName;
818 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
819 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
820 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
821 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
823 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
825 dwRequiredSize += nLocalFilePathSize;
827 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
828 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
829 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
831 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
833 if (*lpdwBufferSize >= dwRequiredSize)
835 lpCacheEntryInfo->lpHeaderInfo = (LPSTR)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
836 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
837 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
839 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
840 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
841 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
843 if (dwRequiredSize > *lpdwBufferSize)
845 *lpdwBufferSize = dwRequiredSize;
846 SetLastError(ERROR_INSUFFICIENT_BUFFER);
847 return FALSE;
849 *lpdwBufferSize = dwRequiredSize;
850 return TRUE;
854 /***********************************************************************
855 * URLCache_SetEntryInfo (Internal)
857 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
858 * according the the flags set by dwFieldControl.
860 * RETURNS
861 * TRUE if the buffer was big enough
862 * FALSE if the buffer was too small
865 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
867 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
868 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
869 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
870 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
871 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
872 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
873 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
874 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
875 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
876 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
877 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
878 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
879 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
880 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
881 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
882 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
884 return TRUE;
887 /***********************************************************************
888 * URLCache_HashKey (Internal)
890 * Returns the hash key for a given string
892 * RETURNS
893 * hash key for the string
896 static DWORD URLCache_HashKey(LPCSTR lpszKey)
898 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
899 * but the algorithm and result are not the same!
901 static const unsigned char lookupTable[256] =
903 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
904 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
905 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
906 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
907 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
908 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
909 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
910 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
911 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
912 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
913 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
914 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
915 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
916 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
917 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
918 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
919 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
920 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
921 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
922 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
923 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
924 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
925 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
926 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
927 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
928 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
929 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
930 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
931 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
932 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
933 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
934 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
936 BYTE key[4];
937 int i;
938 int subscript[sizeof(key) / sizeof(key[0])];
940 subscript[0] = *lpszKey;
941 subscript[1] = (char)(*lpszKey + 1);
942 subscript[2] = (char)(*lpszKey + 2);
943 subscript[3] = (char)(*lpszKey + 3);
945 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
946 key[i] = lookupTable[i];
948 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
950 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
951 key[i] = lookupTable[*lpszKey ^ key[i]];
954 return *(DWORD *)key;
957 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
959 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
962 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
964 /* structure of hash table:
965 * 448 entries divided into 64 blocks
966 * each block therefore contains a chain of 7 key/offset pairs
967 * how position in table is calculated:
968 * 1. the url is hashed in helper function
969 * 2. the key % 64 * 8 is the offset
970 * 3. the key in the hash table is the hash key aligned to 64
972 * note:
973 * there can be multiple hash tables in the file and the offset to
974 * the next one is stored in the header of the hash table
976 DWORD key = URLCache_HashKey(lpszUrl);
977 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
978 HASH_CACHEFILE_ENTRY * pHashEntry;
979 DWORD dwHashTableNumber = 0;
981 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
983 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
984 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
985 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
987 int i;
988 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
990 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
991 continue;
993 /* make sure that it is in fact a hash entry */
994 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
996 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
997 continue;
1000 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1002 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1003 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1005 /* FIXME: we should make sure that this is the right element
1006 * before returning and claiming that it is. We can do this
1007 * by doing a simple compare between the URL we were given
1008 * and the URL stored in the entry. However, this assumes
1009 * we know the format of all the entries stored in the
1010 * hash table */
1011 *ppHashEntry = pHashElement;
1012 return TRUE;
1016 return FALSE;
1019 /***********************************************************************
1020 * URLCache_FindEntryInHash (Internal)
1022 * Searches all the hash tables in the index for the given URL and
1023 * returns the entry, if it was found, in ppEntry
1025 * RETURNS
1026 * TRUE if the entry was found
1027 * FALSE if the entry could not be found
1030 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
1032 struct _HASH_ENTRY * pHashEntry;
1033 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1035 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1036 return TRUE;
1038 return FALSE;
1041 /***********************************************************************
1042 * URLCache_HashEntrySetUse (Internal)
1044 * Searches all the hash tables in the index for the given URL and
1045 * sets the use count (stored or'ed with key)
1047 * RETURNS
1048 * TRUE if the entry was found
1049 * FALSE if the entry could not be found
1052 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1054 struct _HASH_ENTRY * pHashEntry;
1055 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1057 pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1058 return TRUE;
1060 return FALSE;
1063 /***********************************************************************
1064 * URLCache_DeleteEntryFromHash (Internal)
1066 * Searches all the hash tables in the index for the given URL and
1067 * then if found deletes the entry.
1069 * RETURNS
1070 * TRUE if the entry was found
1071 * FALSE if the entry could not be found
1074 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1076 struct _HASH_ENTRY * pHashEntry;
1077 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1079 pHashEntry->dwHashKey = HASHTABLE_FREE;
1080 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1081 return TRUE;
1083 return FALSE;
1086 /***********************************************************************
1087 * URLCache_AddEntryToHash (Internal)
1089 * Searches all the hash tables for a free slot based on the offset
1090 * generated from the hash key. If a free slot is found, the offset and
1091 * key are entered into the hash table.
1093 * RETURNS
1094 * TRUE if the entry was added
1095 * FALSE if the entry could not be added
1098 static BOOL URLCache_AddEntryToHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1100 /* see URLCache_FindEntryInHash for structure of hash tables */
1102 DWORD key = URLCache_HashKey(lpszUrl);
1103 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1104 HASH_CACHEFILE_ENTRY * pHashEntry;
1105 DWORD dwHashTableNumber = 0;
1107 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1109 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1110 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1111 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1113 int i;
1114 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1116 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1117 break;
1119 /* make sure that it is in fact a hash entry */
1120 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1122 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1123 break;
1126 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1128 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1129 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1131 pHashElement->dwHashKey = key;
1132 pHashElement->dwOffsetEntry = dwOffsetEntry;
1133 return TRUE;
1137 FIXME("need to create another hash table\n");
1138 return FALSE;
1141 /***********************************************************************
1142 * GetUrlCacheEntryInfoExA (WININET.@)
1145 BOOL WINAPI GetUrlCacheEntryInfoExA(
1146 LPCSTR lpszUrl,
1147 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1148 LPDWORD lpdwCacheEntryInfoBufSize,
1149 LPSTR lpszReserved,
1150 LPDWORD lpdwReserved,
1151 LPVOID lpReserved,
1152 DWORD dwFlags)
1154 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1155 debugstr_a(lpszUrl),
1156 lpCacheEntryInfo,
1157 lpdwCacheEntryInfoBufSize,
1158 lpszReserved,
1159 lpdwReserved,
1160 lpReserved,
1161 dwFlags);
1163 if ((lpszReserved != NULL) ||
1164 (lpdwReserved != NULL) ||
1165 (lpReserved != NULL))
1167 ERR("Reserved value was not 0\n");
1168 SetLastError(ERROR_INVALID_PARAMETER);
1169 return FALSE;
1171 if (dwFlags != 0)
1172 FIXME("Undocumented flag(s): %lx\n", dwFlags);
1173 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1176 /***********************************************************************
1177 * GetUrlCacheEntryInfoA (WININET.@)
1180 BOOL WINAPI GetUrlCacheEntryInfoA(
1181 IN LPCSTR lpszUrlName,
1182 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1183 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1186 LPURLCACHE_HEADER pHeader;
1187 CACHEFILE_ENTRY * pEntry;
1188 URL_CACHEFILE_ENTRY * pUrlEntry;
1189 URLCACHECONTAINER * pContainer;
1191 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1193 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1194 return FALSE;
1196 if (!URLCacheContainer_OpenIndex(pContainer))
1197 return FALSE;
1199 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1200 return FALSE;
1202 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1204 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1205 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1206 SetLastError(ERROR_FILE_NOT_FOUND);
1207 return FALSE;
1210 if (pEntry->dwSignature != URL_SIGNATURE)
1212 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1213 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1214 SetLastError(ERROR_FILE_NOT_FOUND);
1215 return FALSE;
1218 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1219 TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1220 if (pUrlEntry->dwOffsetHeaderInfo)
1221 TRACE("Header info: %s\n", debugstr_a((LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1223 if (!URLCache_CopyEntry(
1224 pContainer,
1225 pHeader,
1226 lpCacheEntryInfo,
1227 lpdwCacheEntryInfoBufferSize,
1228 pUrlEntry,
1229 FALSE /* ANSI */))
1231 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1232 return FALSE;
1234 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1236 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1238 return TRUE;
1241 /***********************************************************************
1242 * GetUrlCacheEntryInfoW (WININET.@)
1245 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1246 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1247 LPDWORD lpdwCacheEntryInfoBufferSize)
1249 LPURLCACHE_HEADER pHeader;
1250 CACHEFILE_ENTRY * pEntry;
1251 URL_CACHEFILE_ENTRY * pUrlEntry;
1252 URLCACHECONTAINER * pContainer;
1253 LPSTR lpszUrlA;
1254 int url_len;
1256 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1258 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1259 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1260 if (!lpszUrlA)
1262 SetLastError(ERROR_OUTOFMEMORY);
1263 return FALSE;
1265 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1267 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1269 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1270 return FALSE;
1273 if (!URLCacheContainer_OpenIndex(pContainer))
1275 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1276 return FALSE;
1279 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1281 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1282 return FALSE;
1285 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1287 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1288 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1289 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1290 SetLastError(ERROR_FILE_NOT_FOUND);
1291 return FALSE;
1293 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1295 if (pEntry->dwSignature != URL_SIGNATURE)
1297 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1298 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1299 SetLastError(ERROR_FILE_NOT_FOUND);
1300 return FALSE;
1303 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1304 TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1305 TRACE("Header info: %s\n", debugstr_a((LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1307 if (!URLCache_CopyEntry(
1308 pContainer,
1309 pHeader,
1310 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1311 lpdwCacheEntryInfoBufferSize,
1312 pUrlEntry,
1313 TRUE /* UNICODE */))
1315 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1316 return FALSE;
1318 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1320 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1322 return TRUE;
1325 /***********************************************************************
1326 * GetUrlCacheEntryInfoExW (WININET.@)
1329 BOOL WINAPI GetUrlCacheEntryInfoExW(
1330 LPCWSTR lpszUrl,
1331 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1332 LPDWORD lpdwCacheEntryInfoBufSize,
1333 LPWSTR lpszReserved,
1334 LPDWORD lpdwReserved,
1335 LPVOID lpReserved,
1336 DWORD dwFlags)
1338 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1339 debugstr_w(lpszUrl),
1340 lpCacheEntryInfo,
1341 lpdwCacheEntryInfoBufSize,
1342 lpszReserved,
1343 lpdwReserved,
1344 lpReserved,
1345 dwFlags);
1347 if ((lpszReserved != NULL) ||
1348 (lpdwReserved != NULL) ||
1349 (lpReserved != NULL))
1351 ERR("Reserved value was not 0\n");
1352 SetLastError(ERROR_INVALID_PARAMETER);
1353 return FALSE;
1355 if (dwFlags != 0)
1356 FIXME("Undocumented flag(s): %lx\n", dwFlags);
1357 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1360 /***********************************************************************
1361 * SetUrlCacheEntryInfoA (WININET.@)
1363 BOOL WINAPI SetUrlCacheEntryInfoA(
1364 LPCSTR lpszUrlName,
1365 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1366 DWORD dwFieldControl)
1368 LPURLCACHE_HEADER pHeader;
1369 CACHEFILE_ENTRY * pEntry;
1370 URLCACHECONTAINER * pContainer;
1372 TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1374 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1375 return FALSE;
1377 if (!URLCacheContainer_OpenIndex(pContainer))
1378 return FALSE;
1380 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1381 return FALSE;
1383 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1385 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1386 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1387 SetLastError(ERROR_FILE_NOT_FOUND);
1388 return FALSE;
1391 if (pEntry->dwSignature != URL_SIGNATURE)
1393 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1394 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1395 SetLastError(ERROR_FILE_NOT_FOUND);
1396 return FALSE;
1399 URLCache_SetEntryInfo(
1400 (URL_CACHEFILE_ENTRY *)pEntry,
1401 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1402 dwFieldControl);
1404 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1406 return TRUE;
1409 /***********************************************************************
1410 * SetUrlCacheEntryInfoW (WININET.@)
1412 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1414 LPURLCACHE_HEADER pHeader;
1415 CACHEFILE_ENTRY * pEntry;
1416 URLCACHECONTAINER * pContainer;
1417 LPSTR lpszUrlA;
1418 int url_len;
1420 TRACE("(%s, %p, 0x%08lx)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1422 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1423 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1424 if (!lpszUrlA)
1426 SetLastError(ERROR_OUTOFMEMORY);
1427 return FALSE;
1429 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1431 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1433 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1434 return FALSE;
1437 if (!URLCacheContainer_OpenIndex(pContainer))
1439 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1440 return FALSE;
1443 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1445 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1446 return FALSE;
1449 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1451 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1452 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1453 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1454 SetLastError(ERROR_FILE_NOT_FOUND);
1455 return FALSE;
1457 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1459 if (pEntry->dwSignature != URL_SIGNATURE)
1461 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1462 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1463 SetLastError(ERROR_FILE_NOT_FOUND);
1464 return FALSE;
1467 URLCache_SetEntryInfo(
1468 (URL_CACHEFILE_ENTRY *)pEntry,
1469 lpCacheEntryInfo,
1470 dwFieldControl);
1472 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1474 return TRUE;
1477 /***********************************************************************
1478 * RetrieveUrlCacheEntryFileA (WININET.@)
1481 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1482 IN LPCSTR lpszUrlName,
1483 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1484 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1485 IN DWORD dwReserved
1488 LPURLCACHE_HEADER pHeader;
1489 CACHEFILE_ENTRY * pEntry;
1490 URL_CACHEFILE_ENTRY * pUrlEntry;
1491 URLCACHECONTAINER * pContainer;
1493 TRACE("(%s, %p, %p, 0x%08lx)\n",
1494 debugstr_a(lpszUrlName),
1495 lpCacheEntryInfo,
1496 lpdwCacheEntryInfoBufferSize,
1497 dwReserved);
1499 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1500 return FALSE;
1502 if (!URLCacheContainer_OpenIndex(pContainer))
1503 return FALSE;
1505 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1506 return FALSE;
1508 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1510 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1511 TRACE("entry %s not found!\n", lpszUrlName);
1512 SetLastError(ERROR_FILE_NOT_FOUND);
1513 return FALSE;
1516 if (pEntry->dwSignature != URL_SIGNATURE)
1518 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1519 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1520 SetLastError(ERROR_FILE_NOT_FOUND);
1521 return FALSE;
1524 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1525 TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
1526 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1528 pUrlEntry->dwHitRate++;
1529 pUrlEntry->dwUseCount++;
1530 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1532 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1534 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1535 return FALSE;
1537 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1539 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1541 return TRUE;
1544 /***********************************************************************
1545 * UnlockUrlCacheEntryFileA (WININET.@)
1548 BOOL WINAPI UnlockUrlCacheEntryFileA(
1549 IN LPCSTR lpszUrlName,
1550 IN DWORD dwReserved
1553 LPURLCACHE_HEADER pHeader;
1554 CACHEFILE_ENTRY * pEntry;
1555 URL_CACHEFILE_ENTRY * pUrlEntry;
1556 URLCACHECONTAINER * pContainer;
1558 TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName), dwReserved);
1560 if (dwReserved)
1562 ERR("dwReserved != 0\n");
1563 SetLastError(ERROR_INVALID_PARAMETER);
1564 return FALSE;
1567 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1568 return FALSE;
1570 if (!URLCacheContainer_OpenIndex(pContainer))
1571 return FALSE;
1573 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1574 return FALSE;
1576 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1578 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1579 TRACE("entry %s not found!\n", lpszUrlName);
1580 SetLastError(ERROR_FILE_NOT_FOUND);
1581 return FALSE;
1584 if (pEntry->dwSignature != URL_SIGNATURE)
1586 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1587 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1588 SetLastError(ERROR_FILE_NOT_FOUND);
1589 return FALSE;
1592 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1594 if (pUrlEntry->dwUseCount == 0)
1596 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1597 return FALSE;
1599 pUrlEntry->dwUseCount--;
1600 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1602 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1604 return TRUE;
1607 /***********************************************************************
1608 * CreateUrlCacheEntryA (WININET.@)
1611 BOOL WINAPI CreateUrlCacheEntryA(
1612 IN LPCSTR lpszUrlName,
1613 IN DWORD dwExpectedFileSize,
1614 IN LPCSTR lpszFileExtension,
1615 OUT LPSTR lpszFileName,
1616 IN DWORD dwReserved
1619 URLCACHECONTAINER * pContainer;
1620 LPURLCACHE_HEADER pHeader;
1621 CHAR szFile[MAX_PATH];
1622 CHAR szExtension[MAX_PATH];
1623 LPCSTR lpszUrlPart;
1624 LPCSTR lpszUrlEnd;
1625 LPCSTR lpszFileNameExtension;
1626 LPSTR lpszFileNameNoPath;
1627 int i;
1628 int countnoextension;
1629 BYTE CacheDir;
1630 LONG lBufferSize;
1631 BOOL bFound = FALSE;
1632 int count;
1634 TRACE("(%s, 0x%08lx, %s, %p, 0x%08lx)\n",
1635 debugstr_a(lpszUrlName),
1636 dwExpectedFileSize,
1637 debugstr_a(lpszFileExtension),
1638 lpszFileName,
1639 dwReserved);
1641 if (dwReserved)
1643 ERR("dwReserved != 0\n");
1644 SetLastError(ERROR_INVALID_PARAMETER);
1645 return FALSE;
1648 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1651 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1652 lpszUrlEnd--;
1654 for (lpszUrlPart = lpszUrlEnd;
1655 (lpszUrlPart >= lpszUrlName);
1656 lpszUrlPart--)
1658 if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1660 bFound = TRUE;
1661 lpszUrlPart++;
1662 break;
1665 if (!strcmp(lpszUrlPart, "www"))
1667 lpszUrlPart += strlen("www");
1670 count = lpszUrlEnd - lpszUrlPart;
1672 if (bFound && (count < MAX_PATH))
1674 memcpy(szFile, lpszUrlPart, count * sizeof(CHAR));
1675 szFile[count] = '\0';
1676 /* FIXME: get rid of illegal characters like \, / and : */
1678 else
1680 FIXME("need to generate a random filename");
1683 TRACE("File name: %s\n", szFile);
1685 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1686 return FALSE;
1688 if (!URLCacheContainer_OpenIndex(pContainer))
1689 return FALSE;
1691 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1692 return FALSE;
1694 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1696 lBufferSize = MAX_PATH * sizeof(CHAR);
1697 URLCache_LocalFileNameToPathA(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1699 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1701 lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(CHAR) + DIR_LENGTH + 1;
1703 countnoextension = strlen(lpszFileNameNoPath);
1704 lpszFileNameExtension = PathFindExtensionA(lpszFileNameNoPath);
1705 if (lpszFileNameExtension)
1706 countnoextension -= strlen(lpszFileNameExtension);
1707 *szExtension = '\0';
1709 if (lpszFileExtension)
1711 szExtension[0] = '.';
1712 strcpy(szExtension+1, lpszFileExtension);
1715 for (i = 0; i < 255; i++)
1717 HANDLE hFile;
1718 strncpy(lpszFileNameNoPath, szFile, countnoextension);
1719 sprintf(lpszFileNameNoPath + countnoextension, "[%u]%s", i, szExtension);
1720 TRACE("Trying: %s\n", lpszFileName);
1721 hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1722 if (hFile != INVALID_HANDLE_VALUE)
1724 CloseHandle(hFile);
1725 return TRUE;
1729 return FALSE;
1732 /***********************************************************************
1733 * CommitUrlCacheEntryA (WININET.@)
1736 BOOL WINAPI CommitUrlCacheEntryA(
1737 IN LPCSTR lpszUrlName,
1738 IN LPCSTR lpszLocalFileName,
1739 IN FILETIME ExpireTime,
1740 IN FILETIME LastModifiedTime,
1741 IN DWORD CacheEntryType,
1742 IN LPBYTE lpHeaderInfo,
1743 IN DWORD dwHeaderSize,
1744 IN LPCSTR lpszFileExtension,
1745 IN LPCSTR dwReserved
1748 URLCACHECONTAINER * pContainer;
1749 LPURLCACHE_HEADER pHeader;
1750 CACHEFILE_ENTRY * pEntry;
1751 URL_CACHEFILE_ENTRY * pUrlEntry;
1752 DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1753 DWORD dwOffsetLocalFileName = 0;
1754 DWORD dwOffsetHeader = 0;
1755 DWORD dwFileSizeLow = 0;
1756 DWORD dwFileSizeHigh = 0;
1757 BYTE cDirectory = 0;
1759 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1760 debugstr_a(lpszUrlName),
1761 debugstr_a(lpszLocalFileName),
1762 CacheEntryType,
1763 lpHeaderInfo,
1764 dwHeaderSize,
1765 lpszFileExtension,
1766 dwReserved);
1768 if (dwReserved)
1770 ERR("dwReserved != 0\n");
1771 SetLastError(ERROR_INVALID_PARAMETER);
1772 return FALSE;
1774 if (lpHeaderInfo == NULL)
1776 FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1779 if (lpszLocalFileName)
1781 HANDLE hFile;
1782 hFile = CreateFileA(lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1783 if (hFile == INVALID_HANDLE_VALUE)
1785 ERR("couldn't open file %s (error is %ld)\n", debugstr_a(lpszLocalFileName), GetLastError());
1786 return FALSE;
1789 /* Get file size */
1790 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1791 if ((dwFileSizeLow == -1) && (GetLastError() != NO_ERROR))
1793 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1794 CloseHandle(hFile);
1795 return FALSE;
1798 CloseHandle(hFile);
1801 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1802 return FALSE;
1804 if (!URLCacheContainer_OpenIndex(pContainer))
1805 return FALSE;
1807 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1808 return FALSE;
1810 if (URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1812 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1813 FIXME("entry already in cache - don't know what to do!\n");
1815 * SetLastError(ERROR_FILE_NOT_FOUND);
1816 * return FALSE;
1818 return TRUE;
1821 if (lpszLocalFileName)
1823 BOOL bFound = FALSE;
1824 char szContainerPath[MAX_PATH];
1825 int container_path_len;
1826 container_path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szContainerPath, sizeof(szContainerPath), NULL, NULL);
1827 if (!container_path_len)
1829 /* WideCharToMultiByte should have called SetLastError */
1830 return FALSE;
1833 if (strncmp(lpszLocalFileName, szContainerPath, container_path_len))
1835 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1836 ERR("path %s must begin with cache content path %s\n", debugstr_a(lpszLocalFileName), debugstr_a(szContainerPath));
1837 SetLastError(ERROR_INVALID_PARAMETER);
1838 return FALSE;
1841 /* skip container path prefix */
1842 lpszLocalFileName += container_path_len;
1844 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1846 if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1848 bFound = TRUE;
1849 break;
1853 if (!bFound)
1855 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1856 ERR("cache directory not found in path %s\n", lpszLocalFileName);
1857 SetLastError(ERROR_INVALID_PARAMETER);
1858 return FALSE;
1861 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1864 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlName) + 1);
1865 if (lpszLocalFileName)
1867 dwOffsetLocalFileName = dwBytesNeeded;
1868 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszLocalFileName) + 1);
1870 if (lpHeaderInfo)
1872 dwOffsetHeader = dwBytesNeeded;
1873 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1876 /* round up to next block */
1877 if (dwBytesNeeded % BLOCKSIZE)
1879 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1880 dwBytesNeeded += BLOCKSIZE;
1883 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1885 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1886 ERR("no free entries\n");
1887 return FALSE;
1890 /* FindFirstFreeEntry fills in blocks used */
1891 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1892 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1893 pUrlEntry->CacheDir = cDirectory;
1894 pUrlEntry->CacheEntryType = CacheEntryType;
1895 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1896 pUrlEntry->dwExemptDelta = 0;
1897 pUrlEntry->dwHitRate = 0;
1898 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1899 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1900 pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1901 pUrlEntry->dwSizeHigh = 0;
1902 pUrlEntry->dwSizeLow = dwFileSizeLow;
1903 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1904 pUrlEntry->dwUseCount = 0;
1905 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1906 pUrlEntry->LastModifiedTime = LastModifiedTime;
1907 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1908 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1909 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1910 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1912 /*** Unknowns ***/
1913 pUrlEntry->dwUnknown1 = 0;
1914 pUrlEntry->dwUnknown2 = 0;
1915 pUrlEntry->dwUnknown3 = 0x60;
1916 pUrlEntry->Unknown4 = 0;
1917 pUrlEntry->wUnknown5 = 0x1010;
1918 pUrlEntry->dwUnknown6 = 0;
1919 pUrlEntry->dwUnknown7 = 0;
1920 pUrlEntry->dwUnknown8 = 0;
1922 strcpy(pUrlEntry->szSourceUrlName, lpszUrlName);
1923 if (dwOffsetLocalFileName)
1924 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), lpszLocalFileName);
1925 if (dwOffsetHeader)
1926 memcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetHeader), lpHeaderInfo, dwHeaderSize);
1928 if (!URLCache_AddEntryToHash(pHeader, lpszUrlName, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1930 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1931 return FALSE;
1934 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1936 return TRUE;
1939 BOOL WINAPI ReadUrlCacheEntryStream(
1940 IN HANDLE hUrlCacheStream,
1941 IN DWORD dwLocation,
1942 IN OUT LPVOID lpBuffer,
1943 IN OUT LPDWORD lpdwLen,
1944 IN DWORD dwReserved
1947 /* Get handle to file from 'stream' */
1948 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1950 if (dwReserved != 0)
1952 ERR("dwReserved != 0\n");
1953 SetLastError(ERROR_INVALID_PARAMETER);
1954 return FALSE;
1957 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1959 SetLastError(ERROR_INVALID_HANDLE);
1960 return FALSE;
1963 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == -1)
1964 return FALSE;
1965 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
1968 /***********************************************************************
1969 * RetrieveUrlCacheEntryStreamA (WININET.@)
1972 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
1973 IN LPCSTR lpszUrlName,
1974 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1975 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1976 IN BOOL fRandomRead,
1977 IN DWORD dwReserved
1980 /* NOTE: this is not the same as the way that the native
1981 * version allocates 'stream' handles. I did it this way
1982 * as it is much easier and no applications should depend
1983 * on this behaviour. (Native version appears to allocate
1984 * indices into a table)
1986 STREAM_HANDLE * pStream;
1987 HANDLE hFile;
1989 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
1990 lpCacheEntryInfo,
1991 lpdwCacheEntryInfoBufferSize,
1992 dwReserved))
1994 return NULL;
1997 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1998 GENERIC_READ,
1999 FILE_SHARE_READ,
2000 NULL,
2001 OPEN_EXISTING,
2002 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2003 NULL);
2004 if (hFile == INVALID_HANDLE_VALUE)
2005 return FALSE;
2007 /* allocate handle storage space */
2008 pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2009 if (!pStream)
2011 CloseHandle(hFile);
2012 SetLastError(ERROR_OUTOFMEMORY);
2013 return FALSE;
2016 pStream->hFile = hFile;
2017 strcpy(pStream->lpszUrl, lpszUrlName);
2018 return (HANDLE)pStream;
2021 /***********************************************************************
2022 * UnlockUrlCacheEntryStream (WININET.@)
2025 BOOL WINAPI UnlockUrlCacheEntryStream(
2026 IN HANDLE hUrlCacheStream,
2027 IN DWORD dwReserved
2030 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2032 if (dwReserved != 0)
2034 ERR("dwReserved != 0\n");
2035 SetLastError(ERROR_INVALID_PARAMETER);
2036 return FALSE;
2039 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2041 SetLastError(ERROR_INVALID_HANDLE);
2042 return FALSE;
2045 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2046 return FALSE;
2048 /* close file handle */
2049 CloseHandle(pStream->hFile);
2051 /* free allocated space */
2052 HeapFree(GetProcessHeap(), 0, pStream);
2054 return TRUE;
2058 /***********************************************************************
2059 * DeleteUrlCacheEntryA (WININET.@)
2062 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2064 URLCACHECONTAINER * pContainer;
2065 LPURLCACHE_HEADER pHeader;
2066 CACHEFILE_ENTRY * pEntry;
2067 DWORD dwStartBlock;
2068 DWORD dwBlock;
2069 BYTE * AllocationTable;
2071 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2073 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2074 return FALSE;
2076 if (!URLCacheContainer_OpenIndex(pContainer))
2077 return FALSE;
2079 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2080 return FALSE;
2082 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2084 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2085 TRACE("entry %s not found!\n", lpszUrlName);
2086 SetLastError(ERROR_FILE_NOT_FOUND);
2087 return FALSE;
2090 AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
2092 /* update allocation table */
2093 dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
2094 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
2095 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
2097 URLCache_DeleteEntry(pEntry);
2099 URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2101 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2103 return TRUE;
2106 /***********************************************************************
2107 * FindFirstUrlCacheEntryA (WININET.@)
2110 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2111 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2113 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2114 SetLastError(ERROR_FILE_NOT_FOUND);
2115 return 0;
2118 /***********************************************************************
2119 * FindFirstUrlCacheEntryW (WININET.@)
2122 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2123 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2125 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2126 return 0;
2129 /***********************************************************************
2130 * CreateUrlCacheGroup (WININET.@)
2133 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID
2134 lpReserved)
2136 FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
2137 return FALSE;
2140 /***********************************************************************
2141 * DeleteUrlCacheGroup (WININET.@)
2144 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2146 FIXME("STUB\n");
2147 return FALSE;
2150 /***********************************************************************
2151 * SetUrlCacheEntryGroup (WININET.@)
2154 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
2155 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2156 LPVOID lpReserved)
2158 FIXME("STUB\n");
2159 SetLastError(ERROR_FILE_NOT_FOUND);
2160 return FALSE;
2163 /***********************************************************************
2164 * GetUrlCacheConfigInfoW (WININET.@)
2166 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2168 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2169 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2170 return FALSE;
2173 /***********************************************************************
2174 * GetUrlCacheConfigInfoA (WININET.@)
2176 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2178 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2180 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2181 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2182 return FALSE;