Changes in crossover-wine-src-6.1.0 except for configure
[wine/hacks.git] / dlls / wininet / urlcache.c
blobec895a27d0731c68cbae908d19759473286f4619
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
26 #include "wine/port.h"
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winuser.h"
40 #include "wininet.h"
41 #include "winerror.h"
42 #include "internet.h"
43 #include "winreg.h"
44 #include "shlwapi.h"
45 #include "wingdi.h"
46 #include "shlobj.h"
48 #include "wine/unicode.h"
49 #include "wine/list.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
54 #define ENTRY_START_OFFSET 0x4000
55 #define DIR_LENGTH 8
56 #define BLOCKSIZE 128
57 #define HASHTABLE_SIZE 448
58 #define HASHTABLE_BLOCKSIZE 7
59 #define HASHTABLE_FREE 3
60 #define ALLOCATION_TABLE_OFFSET 0x250
61 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
62 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
63 #define NEWFILE_NUM_BLOCKS 0xd80
64 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
66 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
67 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
68 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
69 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
70 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
72 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
74 typedef struct _CACHEFILE_ENTRY
76 /* union
77 {*/
78 DWORD dwSignature; /* e.g. "URL " */
79 /* CHAR szSignature[4];
80 };*/
81 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
82 } CACHEFILE_ENTRY;
84 typedef struct _URL_CACHEFILE_ENTRY
86 CACHEFILE_ENTRY CacheFileEntry;
87 FILETIME LastModifiedTime;
88 FILETIME LastAccessTime;
89 WORD wExpiredDate; /* expire date in dos format */
90 WORD wExpiredTime; /* expire time in dos format */
91 DWORD dwUnknown1; /* usually zero */
92 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
93 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
94 DWORD dwUnknown2; /* usually zero */
95 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
96 DWORD dwUnknown3; /* usually 0x60 */
97 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
98 BYTE CacheDir; /* index of cache directory this url is stored in */
99 BYTE Unknown4; /* usually zero */
100 WORD wUnknown5; /* usually 0x1010 */
101 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
102 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
103 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
104 DWORD dwHeaderInfoSize;
105 DWORD dwUnknown6; /* usually zero */
106 WORD wLastSyncDate; /* last sync date in dos format */
107 WORD wLastSyncTime; /* last sync time in dos format */
108 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
109 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
110 WORD wUnknownDate; /* usually same as wLastSyncDate */
111 WORD wUnknownTime; /* usually same as wLastSyncTime */
112 DWORD dwUnknown7; /* usually zero */
113 DWORD dwUnknown8; /* usually zero */
114 /* packing to dword align start of next field */
115 /* CHAR szSourceUrlName[]; (url) */
116 /* packing to dword align start of next field */
117 /* CHAR szLocalFileName[]; (local file name exluding path) */
118 /* packing to dword align start of next field */
119 /* CHAR szHeaderInfo[]; (header info) */
120 } URL_CACHEFILE_ENTRY;
122 struct _HASH_ENTRY
124 DWORD dwHashKey;
125 DWORD dwOffsetEntry;
128 typedef struct _HASH_CACHEFILE_ENTRY
130 CACHEFILE_ENTRY CacheFileEntry;
131 DWORD dwAddressNext;
132 DWORD dwHashTableNumber;
133 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
134 } HASH_CACHEFILE_ENTRY;
136 typedef struct _DIRECTORY_DATA
138 DWORD dwUnknown;
139 char filename[DIR_LENGTH];
140 } DIRECTORY_DATA;
142 typedef struct _URLCACHE_HEADER
144 char szSignature[28];
145 DWORD dwFileSize;
146 DWORD dwOffsetFirstHashTable;
147 DWORD dwIndexCapacityInBlocks;
148 DWORD dwBlocksInUse;
149 DWORD dwUnknown1;
150 DWORD dwCacheLimitLow; /* disk space limit for cache */
151 DWORD dwCacheLimitHigh; /* disk space limit for cache */
152 DWORD dwUnknown4; /* current disk space usage for cache */
153 DWORD dwUnknown5; /* current disk space usage for cache */
154 DWORD dwUnknown6; /* possibly a flag? */
155 DWORD dwUnknown7;
156 BYTE DirectoryCount; /* number of directory_data's */
157 BYTE Unknown8[3]; /* just padding? */
158 DIRECTORY_DATA directory_data[1]; /* first directory entry */
159 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
160 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
162 typedef struct _STREAM_HANDLE
164 HANDLE hFile;
165 CHAR lpszUrl[1];
166 } STREAM_HANDLE;
168 typedef struct _URLCACHECONTAINER
170 struct list entry; /* part of a list */
171 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
172 LPWSTR path; /* path to url container directory */
173 HANDLE hMapping; /* handle of file mapping */
174 DWORD file_size; /* size of file when mapping was opened */
175 HANDLE hMutex; /* hande of mutex */
176 } URLCACHECONTAINER;
179 /* List of all containers available */
180 static struct list UrlContainers = LIST_INIT(UrlContainers);
182 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash);
184 /***********************************************************************
185 * URLCache_PathToObjectName (Internal)
187 * Converts a path to a name suitable for use as a Win32 object name.
188 * Replaces '\\' characters in-place with the specified character
189 * (usually '_' or '!')
191 * RETURNS
192 * nothing
195 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
197 for (; *lpszPath; lpszPath++)
199 if (*lpszPath == '\\')
200 *lpszPath = replace;
204 /***********************************************************************
205 * URLCacheContainer_OpenIndex (Internal)
207 * Opens the index file and saves mapping handle in hCacheIndexMapping
209 * RETURNS
210 * TRUE if succeeded
211 * FALSE if failed
214 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
216 HANDLE hFile;
217 WCHAR wszFilePath[MAX_PATH];
218 DWORD dwFileSize;
220 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
221 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
223 if (pContainer->hMapping)
224 return TRUE;
226 strcpyW(wszFilePath, pContainer->path);
227 strcatW(wszFilePath, wszIndex);
229 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
230 if (hFile == INVALID_HANDLE_VALUE)
232 /* Maybe the directory wasn't there? Try to create it */
233 if (CreateDirectoryW(pContainer->path, 0))
234 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
236 if (hFile == INVALID_HANDLE_VALUE)
238 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
239 return FALSE;
242 /* At this stage we need the mutex because we may be about to create the
243 * file.
245 WaitForSingleObject(pContainer->hMutex, INFINITE);
247 dwFileSize = GetFileSize(hFile, NULL);
248 if (dwFileSize == INVALID_FILE_SIZE)
250 ReleaseMutex(pContainer->hMutex);
251 return FALSE;
254 if (dwFileSize == 0)
256 static CHAR const szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Cache\\Content";
257 HKEY key;
258 char achZeroes[0x1000];
259 DWORD dwOffset;
260 DWORD dwError = 0;
262 /* Write zeroes to the entire file so we can safely map it without
263 * fear of getting a SEGV because the disk is full.
265 memset(achZeroes, 0, sizeof(achZeroes));
266 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
268 DWORD dwWrite = sizeof(achZeroes);
269 DWORD dwWritten;
271 if (NEWFILE_SIZE - dwOffset < dwWrite)
272 dwWrite = NEWFILE_SIZE - dwOffset;
273 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
274 dwWritten != dwWrite)
276 /* If we fail to write, we need to return the error that
277 * cause the problem and also make sure the file is no
278 * longer there, if possible.
280 dwError = GetLastError();
282 break;
286 if (!dwError)
288 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
290 if (hMapping)
292 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
294 if (pHeader)
296 WCHAR *pwchDir;
297 WCHAR wszDirPath[MAX_PATH];
298 FILETIME ft;
299 int i, j;
301 dwFileSize = NEWFILE_SIZE;
303 /* First set some constants and defaults in the header */
304 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
305 pHeader->dwFileSize = dwFileSize;
306 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
307 /* 127MB - taken from default for Windows 2000 */
308 pHeader->dwCacheLimitHigh = 0;
309 pHeader->dwCacheLimitLow = 0x07ff5400;
310 /* Copied from a Windows 2000 cache index */
311 pHeader->DirectoryCount = 4;
313 /* If the registry has a cache size set, use the registry value */
314 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
316 DWORD dw;
317 DWORD len = sizeof(dw);
318 DWORD keytype;
320 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
321 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
322 keytype == REG_DWORD)
324 pHeader->dwCacheLimitHigh = (dw >> 22);
325 pHeader->dwCacheLimitLow = dw << 10;
327 RegCloseKey(key);
330 URLCache_CreateHashTable(pHeader, NULL);
332 /* Last step - create the directories */
334 strcpyW(wszDirPath, pContainer->path);
335 pwchDir = wszDirPath + strlenW(wszDirPath);
336 pwchDir[8] = 0;
338 GetSystemTimeAsFileTime(&ft);
340 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
342 /* The following values were copied from a Windows index.
343 * I don't know what the values are supposed to mean but
344 * have made them the same in the hope that this will
345 * be better for compatibility
347 pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
348 for (j = 0;; ++j)
350 int k;
351 ULONGLONG n = ft.dwHighDateTime;
353 /* Generate a file name to attempt to create.
354 * This algorithm will create what will appear
355 * to be random and unrelated directory names
356 * of up to 9 characters in length.
358 n <<= 32;
359 n += ft.dwLowDateTime;
360 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
362 for (k = 0; k < 8; ++k)
364 int r = (n % 36);
366 /* Dividing by a prime greater than 36 helps
367 * with the appearance of randomness
369 n /= 37;
371 if (r < 10)
372 pwchDir[k] = '0' + r;
373 else
374 pwchDir[k] = 'A' + (r - 10);
377 if (CreateDirectoryW(wszDirPath, 0))
379 int k;
381 /* The following is OK because we generated an
382 * 8 character directory name made from characters
383 * [A-Z0-9], which are equivalent for all code
384 * pages and for UTF-16
386 for (k = 0; k < 8; ++k)
387 pHeader->directory_data[i].filename[k] = pwchDir[k];
388 break;
390 else if (j >= 255)
392 /* Give up. The most likely cause of this
393 * is a full disk, but whatever the cause
394 * is, it should be more than apparent that
395 * we won't succeed.
397 dwError = GetLastError();
398 break;
403 UnmapViewOfFile(pHeader);
405 else
407 dwError = GetLastError();
409 CloseHandle(hMapping);
411 else
413 dwError = GetLastError();
417 if (dwError)
419 CloseHandle(hFile);
420 DeleteFileW(wszFilePath);
421 ReleaseMutex(pContainer->hMutex);
422 SetLastError(dwError);
423 return FALSE;
428 ReleaseMutex(pContainer->hMutex);
430 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
431 URLCache_PathToObjectName(wszFilePath, '_');
432 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
433 if (!pContainer->hMapping)
434 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
435 CloseHandle(hFile);
436 if (!pContainer->hMapping)
438 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
439 return FALSE;
442 return TRUE;
445 /***********************************************************************
446 * URLCacheContainer_CloseIndex (Internal)
448 * Closes the index
450 * RETURNS
451 * nothing
454 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
456 CloseHandle(pContainer->hMapping);
457 pContainer->hMapping = NULL;
460 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
462 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
463 int path_len = strlenW(path);
464 int cache_prefix_len = strlenW(cache_prefix);
466 if (!pContainer)
468 return FALSE;
471 pContainer->hMapping = NULL;
472 pContainer->file_size = 0;
474 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
475 if (!pContainer->path)
477 HeapFree(GetProcessHeap(), 0, pContainer);
478 return FALSE;
481 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
483 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
484 if (!pContainer->cache_prefix)
486 HeapFree(GetProcessHeap(), 0, pContainer->path);
487 HeapFree(GetProcessHeap(), 0, pContainer);
488 return FALSE;
491 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
493 CharLowerW(mutex_name);
494 URLCache_PathToObjectName(mutex_name, '!');
496 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
498 ERR("couldn't create mutex (error is %d)\n", GetLastError());
499 HeapFree(GetProcessHeap(), 0, pContainer->path);
500 HeapFree(GetProcessHeap(), 0, pContainer);
501 return FALSE;
504 list_add_head(&UrlContainers, &pContainer->entry);
506 return TRUE;
509 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
511 list_remove(&pContainer->entry);
513 URLCacheContainer_CloseIndex(pContainer);
514 CloseHandle(pContainer->hMutex);
515 HeapFree(GetProcessHeap(), 0, pContainer->path);
516 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
517 HeapFree(GetProcessHeap(), 0, pContainer);
520 void URLCacheContainers_CreateDefaults(void)
522 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
523 static const WCHAR UrlPrefix[] = {0};
524 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
525 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
526 static const WCHAR CookieSuffix[] = {0};
527 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
528 static const struct
530 int nFolder; /* CSIDL_* constant */
531 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
532 const WCHAR * cache_prefix; /* prefix used to reference the container */
533 } DefaultContainerData[] =
535 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
536 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
537 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
539 DWORD i;
541 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
543 WCHAR wszCachePath[MAX_PATH];
544 WCHAR wszMutexName[MAX_PATH];
545 int path_len, suffix_len;
547 if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
549 ERR("Couldn't get path for default container %u\n", i);
550 continue;
552 path_len = strlenW(wszCachePath);
553 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
555 if (path_len + suffix_len + 2 > MAX_PATH)
557 ERR("Path too long\n");
558 continue;
561 wszCachePath[path_len] = '\\';
562 wszCachePath[path_len+1] = 0;
564 strcpyW(wszMutexName, wszCachePath);
566 if (suffix_len)
568 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
569 wszCachePath[path_len + suffix_len + 1] = '\\';
570 wszCachePath[path_len + suffix_len + 2] = '\0';
573 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
577 void URLCacheContainers_DeleteAll(void)
579 while(!list_empty(&UrlContainers))
580 URLCacheContainer_DeleteContainer(
581 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
585 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
587 struct list * cursor;
589 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
591 LIST_FOR_EACH(cursor, &UrlContainers)
593 URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
594 int prefix_len = strlenW(pContainer->cache_prefix);
595 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
597 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
598 *ppContainer = pContainer;
599 return TRUE;
602 ERR("no container found\n");
603 SetLastError(ERROR_FILE_NOT_FOUND);
604 return FALSE;
607 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
609 BOOL ret;
610 LPWSTR lpwszUrl;
611 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
612 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
614 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
615 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
616 HeapFree(GetProcessHeap(), 0, lpwszUrl);
617 return ret;
619 return FALSE;
622 /***********************************************************************
623 * URLCacheContainer_LockIndex (Internal)
626 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
628 BYTE index;
629 LPVOID pIndexData;
630 URLCACHE_HEADER * pHeader;
632 /* acquire mutex */
633 WaitForSingleObject(pContainer->hMutex, INFINITE);
635 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
637 if (!pIndexData)
639 ReleaseMutex(pContainer->hMutex);
640 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
641 return FALSE;
643 pHeader = (URLCACHE_HEADER *)pIndexData;
645 /* file has grown - we need to remap to prevent us getting
646 * access violations when we try and access beyond the end
647 * of the memory mapped file */
648 if (pHeader->dwFileSize != pContainer->file_size)
650 URLCacheContainer_CloseIndex(pContainer);
651 if (!URLCacheContainer_OpenIndex(pContainer))
653 ReleaseMutex(pContainer->hMutex);
654 return FALSE;
656 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
658 if (!pIndexData)
660 ReleaseMutex(pContainer->hMutex);
661 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
662 return FALSE;
664 pHeader = (URLCACHE_HEADER *)pIndexData;
667 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
669 for (index = 0; index < pHeader->DirectoryCount; index++)
671 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
674 return pHeader;
677 /***********************************************************************
678 * URLCacheContainer_UnlockIndex (Internal)
681 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
683 /* release mutex */
684 ReleaseMutex(pContainer->hMutex);
685 return UnmapViewOfFile(pHeader);
689 #ifndef CHAR_BIT
690 #define CHAR_BIT (8 * sizeof(CHAR))
691 #endif
693 /***********************************************************************
694 * URLCache_Allocation_BlockIsFree (Internal)
696 * Is the specified block number free?
698 * RETURNS
699 * zero if free
700 * non-zero otherwise
703 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
705 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
706 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
709 /***********************************************************************
710 * URLCache_Allocation_BlockFree (Internal)
712 * Marks the specified block as free
714 * RETURNS
715 * nothing
718 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
720 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
721 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
724 /***********************************************************************
725 * URLCache_Allocation_BlockAllocate (Internal)
727 * Marks the specified block as allocated
729 * RETURNS
730 * nothing
733 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
735 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
736 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
739 /***********************************************************************
740 * URLCache_FindFirstFreeEntry (Internal)
742 * Finds and allocates the first block of free space big enough and
743 * sets ppEntry to point to it.
745 * RETURNS
746 * TRUE if it had enough space
747 * FALSE if it couldn't find enough space
750 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
752 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
753 DWORD dwBlockNumber;
754 DWORD dwFreeCounter;
755 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
757 for (dwFreeCounter = 0;
758 dwFreeCounter < dwBlocksNeeded &&
759 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
760 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
761 dwFreeCounter++)
762 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
764 if (dwFreeCounter == dwBlocksNeeded)
766 DWORD index;
767 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
768 for (index = 0; index < dwBlocksNeeded; index++)
769 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
770 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
771 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
772 return TRUE;
775 FIXME("Grow file\n");
776 return FALSE;
779 /***********************************************************************
780 * URLCache_DeleteEntry (Internal)
782 * Deletes the specified entry and frees the space allocated to it
784 * RETURNS
785 * TRUE if it succeeded
786 * FALSE if it failed
789 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
791 DWORD dwStartBlock;
792 DWORD dwBlock;
793 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
795 /* update allocation table */
796 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
797 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
798 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
800 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
801 return TRUE;
804 /***********************************************************************
805 * URLCache_LocalFileNameToPathW (Internal)
807 * Copies the full path to the specified buffer given the local file
808 * name and the index of the directory it is in. Always sets value in
809 * lpBufferSize to the required buffer size (in bytes).
811 * RETURNS
812 * TRUE if the buffer was big enough
813 * FALSE if the buffer was too small
816 static BOOL URLCache_LocalFileNameToPathW(
817 const URLCACHECONTAINER * pContainer,
818 LPCURLCACHE_HEADER pHeader,
819 LPCSTR szLocalFileName,
820 BYTE Directory,
821 LPWSTR wszPath,
822 LPLONG lpBufferSize)
824 LONG nRequired;
825 int path_len = strlenW(pContainer->path);
826 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
827 if (Directory >= pHeader->DirectoryCount)
829 *lpBufferSize = 0;
830 return FALSE;
833 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
834 if (nRequired < *lpBufferSize)
836 int dir_len;
838 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
839 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
840 wszPath[dir_len + path_len] = '\\';
841 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
842 *lpBufferSize = nRequired;
843 return TRUE;
845 *lpBufferSize = nRequired;
846 return FALSE;
849 /***********************************************************************
850 * URLCache_LocalFileNameToPathA (Internal)
852 * Copies the full path to the specified buffer given the local file
853 * name and the index of the directory it is in. Always sets value in
854 * lpBufferSize to the required buffer size.
856 * RETURNS
857 * TRUE if the buffer was big enough
858 * FALSE if the buffer was too small
861 static BOOL URLCache_LocalFileNameToPathA(
862 const URLCACHECONTAINER * pContainer,
863 LPCURLCACHE_HEADER pHeader,
864 LPCSTR szLocalFileName,
865 BYTE Directory,
866 LPSTR szPath,
867 LPLONG lpBufferSize)
869 LONG nRequired;
870 int path_len, file_name_len, dir_len;
872 if (Directory >= pHeader->DirectoryCount)
874 *lpBufferSize = 0;
875 return FALSE;
878 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
879 file_name_len = strlen(szLocalFileName);
880 dir_len = DIR_LENGTH;
882 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(WCHAR);
883 if (nRequired < *lpBufferSize)
885 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
886 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
887 szPath[path_len + dir_len] = '\\';
888 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
889 *lpBufferSize = nRequired;
890 return TRUE;
892 *lpBufferSize = nRequired;
893 return FALSE;
896 /***********************************************************************
897 * URLCache_CopyEntry (Internal)
899 * Copies an entry from the cache index file to the Win32 structure
901 * RETURNS
902 * TRUE if the buffer was big enough
903 * FALSE if the buffer was too small
906 static BOOL URLCache_CopyEntry(
907 URLCACHECONTAINER * pContainer,
908 LPCURLCACHE_HEADER pHeader,
909 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
910 LPDWORD lpdwBufferSize,
911 URL_CACHEFILE_ENTRY * pUrlEntry,
912 BOOL bUnicode)
914 int lenUrl;
915 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
917 if (*lpdwBufferSize >= dwRequiredSize)
919 lpCacheEntryInfo->lpHeaderInfo = NULL;
920 lpCacheEntryInfo->lpszFileExtension = NULL;
921 lpCacheEntryInfo->lpszLocalFileName = NULL;
922 lpCacheEntryInfo->lpszSourceUrlName = NULL;
923 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
924 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
925 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
926 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
927 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
928 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
929 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
930 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
931 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
932 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
933 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
934 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
935 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
936 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
939 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
940 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
941 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
942 if (bUnicode)
943 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
944 else
945 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
946 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
948 /* FIXME: is source url optional? */
949 if (*lpdwBufferSize >= dwRequiredSize)
951 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
952 if (bUnicode)
953 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
954 else
955 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
958 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
959 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
960 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
962 if (pUrlEntry->dwOffsetLocalName)
964 LONG nLocalFilePathSize;
965 LPSTR lpszLocalFileName;
966 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
967 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
968 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
969 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
971 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
973 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
975 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
976 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
977 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
979 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
981 if (*lpdwBufferSize >= dwRequiredSize)
983 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
984 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
985 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
987 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
988 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
989 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
991 if (dwRequiredSize > *lpdwBufferSize)
993 *lpdwBufferSize = dwRequiredSize;
994 SetLastError(ERROR_INSUFFICIENT_BUFFER);
995 return FALSE;
997 *lpdwBufferSize = dwRequiredSize;
998 return TRUE;
1002 /***********************************************************************
1003 * URLCache_SetEntryInfo (Internal)
1005 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1006 * according the the flags set by dwFieldControl.
1008 * RETURNS
1009 * TRUE if the buffer was big enough
1010 * FALSE if the buffer was too small
1013 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1015 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1016 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1017 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1018 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1019 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1020 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1021 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1022 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1023 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1024 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1025 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1026 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1027 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1028 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1029 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1030 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1032 return TRUE;
1035 /***********************************************************************
1036 * URLCache_HashKey (Internal)
1038 * Returns the hash key for a given string
1040 * RETURNS
1041 * hash key for the string
1044 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1046 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1047 * but the algorithm and result are not the same!
1049 static const unsigned char lookupTable[256] =
1051 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1052 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1053 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1054 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1055 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1056 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1057 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1058 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1059 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1060 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1061 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1062 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1063 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1064 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1065 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1066 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1067 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1068 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1069 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1070 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1071 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1072 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1073 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1074 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1075 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1076 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1077 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1078 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1079 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1080 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1081 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1082 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1084 BYTE key[4];
1085 DWORD i;
1086 int subscript[sizeof(key) / sizeof(key[0])];
1088 subscript[0] = *lpszKey;
1089 subscript[1] = (char)(*lpszKey + 1);
1090 subscript[2] = (char)(*lpszKey + 2);
1091 subscript[3] = (char)(*lpszKey + 3);
1093 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1094 key[i] = lookupTable[i];
1096 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1098 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1099 key[i] = lookupTable[*lpszKey ^ key[i]];
1102 return *(DWORD *)key;
1105 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1107 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1110 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1112 /* structure of hash table:
1113 * 448 entries divided into 64 blocks
1114 * each block therefore contains a chain of 7 key/offset pairs
1115 * how position in table is calculated:
1116 * 1. the url is hashed in helper function
1117 * 2. the key % 64 * 8 is the offset
1118 * 3. the key in the hash table is the hash key aligned to 64
1120 * note:
1121 * there can be multiple hash tables in the file and the offset to
1122 * the next one is stored in the header of the hash table
1124 DWORD key = URLCache_HashKey(lpszUrl);
1125 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1126 HASH_CACHEFILE_ENTRY * pHashEntry;
1127 DWORD dwHashTableNumber = 0;
1129 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1131 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1132 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1133 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1135 int i;
1136 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1138 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1139 continue;
1141 /* make sure that it is in fact a hash entry */
1142 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1144 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1145 continue;
1148 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1150 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1151 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1153 /* FIXME: we should make sure that this is the right element
1154 * before returning and claiming that it is. We can do this
1155 * by doing a simple compare between the URL we were given
1156 * and the URL stored in the entry. However, this assumes
1157 * we know the format of all the entries stored in the
1158 * hash table */
1159 *ppHashEntry = pHashElement;
1160 return TRUE;
1164 return FALSE;
1167 /***********************************************************************
1168 * URLCache_FindEntryInHash (Internal)
1170 * Searches all the hash tables in the index for the given URL and
1171 * returns the entry, if it was found, in ppEntry
1173 * RETURNS
1174 * TRUE if the entry was found
1175 * FALSE if the entry could not be found
1178 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
1180 struct _HASH_ENTRY * pHashEntry;
1181 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1183 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1184 return TRUE;
1186 return FALSE;
1189 /***********************************************************************
1190 * URLCache_HashEntrySetUse (Internal)
1192 * Searches all the hash tables in the index for the given URL and
1193 * sets the use count (stored or'ed with key)
1195 * RETURNS
1196 * TRUE if the entry was found
1197 * FALSE if the entry could not be found
1200 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1202 struct _HASH_ENTRY * pHashEntry;
1203 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1205 pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1206 return TRUE;
1208 return FALSE;
1211 /***********************************************************************
1212 * URLCache_DeleteEntryFromHash (Internal)
1214 * Searches all the hash tables in the index for the given URL and
1215 * then if found deletes the entry.
1217 * RETURNS
1218 * TRUE if the entry was found
1219 * FALSE if the entry could not be found
1222 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1224 struct _HASH_ENTRY * pHashEntry;
1225 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1227 pHashEntry->dwHashKey = HASHTABLE_FREE;
1228 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1229 return TRUE;
1231 return FALSE;
1234 /***********************************************************************
1235 * URLCache_AddEntryToHash (Internal)
1237 * Searches all the hash tables for a free slot based on the offset
1238 * generated from the hash key. If a free slot is found, the offset and
1239 * key are entered into the hash table.
1241 * RETURNS
1242 * TRUE if the entry was added
1243 * FALSE if the entry could not be added
1246 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1248 /* see URLCache_FindEntryInHash for structure of hash tables */
1250 DWORD key = URLCache_HashKey(lpszUrl);
1251 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1252 HASH_CACHEFILE_ENTRY * pHashEntry;
1253 DWORD dwHashTableNumber = 0;
1255 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1257 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1258 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1259 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1261 int i;
1262 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1264 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1265 break;
1267 /* make sure that it is in fact a hash entry */
1268 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1270 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1271 break;
1274 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1276 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1277 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1279 pHashElement->dwHashKey = key;
1280 pHashElement->dwOffsetEntry = dwOffsetEntry;
1281 return TRUE;
1285 pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1286 if (!pHashEntry)
1287 return FALSE;
1289 pHashEntry->HashTable[offset].dwHashKey = key;
1290 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1291 return TRUE;
1294 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1296 HASH_CACHEFILE_ENTRY *pHash;
1297 DWORD dwOffset;
1298 int i;
1300 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1302 FIXME("no free space for hash table\n");
1303 SetLastError(ERROR_DISK_FULL);
1304 return NULL;
1307 dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1309 if (pPrevHash)
1310 pPrevHash->dwAddressNext = dwOffset;
1311 else
1312 pHeader->dwOffsetFirstHashTable = dwOffset;
1313 pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1314 pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1315 pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1316 for (i = 0; i < HASHTABLE_SIZE; i++)
1318 pHash->HashTable[i].dwOffsetEntry = 0;
1319 pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1321 return pHash;
1324 /***********************************************************************
1325 * GetUrlCacheEntryInfoExA (WININET.@)
1328 BOOL WINAPI GetUrlCacheEntryInfoExA(
1329 LPCSTR lpszUrl,
1330 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1331 LPDWORD lpdwCacheEntryInfoBufSize,
1332 LPSTR lpszReserved,
1333 LPDWORD lpdwReserved,
1334 LPVOID lpReserved,
1335 DWORD dwFlags)
1337 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1338 debugstr_a(lpszUrl),
1339 lpCacheEntryInfo,
1340 lpdwCacheEntryInfoBufSize,
1341 lpszReserved,
1342 lpdwReserved,
1343 lpReserved,
1344 dwFlags);
1346 if ((lpszReserved != NULL) ||
1347 (lpdwReserved != NULL) ||
1348 (lpReserved != NULL))
1350 ERR("Reserved value was not 0\n");
1351 SetLastError(ERROR_INVALID_PARAMETER);
1352 return FALSE;
1354 if (dwFlags != 0)
1355 FIXME("Undocumented flag(s): %x\n", dwFlags);
1356 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1359 /***********************************************************************
1360 * GetUrlCacheEntryInfoA (WININET.@)
1363 BOOL WINAPI GetUrlCacheEntryInfoA(
1364 IN LPCSTR lpszUrlName,
1365 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1366 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1369 LPURLCACHE_HEADER pHeader;
1370 CACHEFILE_ENTRY * pEntry;
1371 URL_CACHEFILE_ENTRY * pUrlEntry;
1372 URLCACHECONTAINER * pContainer;
1374 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1376 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1377 return FALSE;
1379 if (!URLCacheContainer_OpenIndex(pContainer))
1380 return FALSE;
1382 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1383 return FALSE;
1385 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1387 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1388 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1389 SetLastError(ERROR_FILE_NOT_FOUND);
1390 return FALSE;
1393 if (pEntry->dwSignature != URL_SIGNATURE)
1395 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1396 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1397 SetLastError(ERROR_FILE_NOT_FOUND);
1398 return FALSE;
1401 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1402 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1403 if (pUrlEntry->dwOffsetHeaderInfo)
1404 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1406 if (!URLCache_CopyEntry(
1407 pContainer,
1408 pHeader,
1409 lpCacheEntryInfo,
1410 lpdwCacheEntryInfoBufferSize,
1411 pUrlEntry,
1412 FALSE /* ANSI */))
1414 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1415 return FALSE;
1417 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1419 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1421 return TRUE;
1424 /***********************************************************************
1425 * GetUrlCacheEntryInfoW (WININET.@)
1428 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1429 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1430 LPDWORD lpdwCacheEntryInfoBufferSize)
1432 LPURLCACHE_HEADER pHeader;
1433 CACHEFILE_ENTRY * pEntry;
1434 URL_CACHEFILE_ENTRY * pUrlEntry;
1435 URLCACHECONTAINER * pContainer;
1436 LPSTR lpszUrlA;
1437 int url_len;
1439 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1441 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1442 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1443 if (!lpszUrlA)
1445 SetLastError(ERROR_OUTOFMEMORY);
1446 return FALSE;
1448 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1450 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1452 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1453 return FALSE;
1456 if (!URLCacheContainer_OpenIndex(pContainer))
1458 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1459 return FALSE;
1462 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1464 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1465 return FALSE;
1468 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1470 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1471 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1472 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1473 SetLastError(ERROR_FILE_NOT_FOUND);
1474 return FALSE;
1476 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1478 if (pEntry->dwSignature != URL_SIGNATURE)
1480 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1481 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1482 SetLastError(ERROR_FILE_NOT_FOUND);
1483 return FALSE;
1486 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1487 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1488 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1490 if (!URLCache_CopyEntry(
1491 pContainer,
1492 pHeader,
1493 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1494 lpdwCacheEntryInfoBufferSize,
1495 pUrlEntry,
1496 TRUE /* UNICODE */))
1498 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1499 return FALSE;
1501 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1503 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1505 return TRUE;
1508 /***********************************************************************
1509 * GetUrlCacheEntryInfoExW (WININET.@)
1512 BOOL WINAPI GetUrlCacheEntryInfoExW(
1513 LPCWSTR lpszUrl,
1514 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1515 LPDWORD lpdwCacheEntryInfoBufSize,
1516 LPWSTR lpszReserved,
1517 LPDWORD lpdwReserved,
1518 LPVOID lpReserved,
1519 DWORD dwFlags)
1521 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1522 debugstr_w(lpszUrl),
1523 lpCacheEntryInfo,
1524 lpdwCacheEntryInfoBufSize,
1525 lpszReserved,
1526 lpdwReserved,
1527 lpReserved,
1528 dwFlags);
1530 if ((lpszReserved != NULL) ||
1531 (lpdwReserved != NULL) ||
1532 (lpReserved != NULL))
1534 ERR("Reserved value was not 0\n");
1535 SetLastError(ERROR_INVALID_PARAMETER);
1536 return FALSE;
1538 if (dwFlags != 0)
1539 FIXME("Undocumented flag(s): %x\n", dwFlags);
1540 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1543 /***********************************************************************
1544 * SetUrlCacheEntryInfoA (WININET.@)
1546 BOOL WINAPI SetUrlCacheEntryInfoA(
1547 LPCSTR lpszUrlName,
1548 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1549 DWORD dwFieldControl)
1551 LPURLCACHE_HEADER pHeader;
1552 CACHEFILE_ENTRY * pEntry;
1553 URLCACHECONTAINER * pContainer;
1555 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1557 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1558 return FALSE;
1560 if (!URLCacheContainer_OpenIndex(pContainer))
1561 return FALSE;
1563 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1564 return FALSE;
1566 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1568 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1569 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1570 SetLastError(ERROR_FILE_NOT_FOUND);
1571 return FALSE;
1574 if (pEntry->dwSignature != URL_SIGNATURE)
1576 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1577 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1578 SetLastError(ERROR_FILE_NOT_FOUND);
1579 return FALSE;
1582 URLCache_SetEntryInfo(
1583 (URL_CACHEFILE_ENTRY *)pEntry,
1584 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1585 dwFieldControl);
1587 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1589 return TRUE;
1592 /***********************************************************************
1593 * SetUrlCacheEntryInfoW (WININET.@)
1595 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1597 LPURLCACHE_HEADER pHeader;
1598 CACHEFILE_ENTRY * pEntry;
1599 URLCACHECONTAINER * pContainer;
1600 LPSTR lpszUrlA;
1601 int url_len;
1603 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1605 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1606 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1607 if (!lpszUrlA)
1609 SetLastError(ERROR_OUTOFMEMORY);
1610 return FALSE;
1612 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1614 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1616 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1617 return FALSE;
1620 if (!URLCacheContainer_OpenIndex(pContainer))
1622 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1623 return FALSE;
1626 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1628 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1629 return FALSE;
1632 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1634 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1635 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1636 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1637 SetLastError(ERROR_FILE_NOT_FOUND);
1638 return FALSE;
1640 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1642 if (pEntry->dwSignature != URL_SIGNATURE)
1644 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1645 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1646 SetLastError(ERROR_FILE_NOT_FOUND);
1647 return FALSE;
1650 URLCache_SetEntryInfo(
1651 (URL_CACHEFILE_ENTRY *)pEntry,
1652 lpCacheEntryInfo,
1653 dwFieldControl);
1655 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1657 return TRUE;
1660 /***********************************************************************
1661 * RetrieveUrlCacheEntryFileA (WININET.@)
1664 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1665 IN LPCSTR lpszUrlName,
1666 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1667 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1668 IN DWORD dwReserved
1671 LPURLCACHE_HEADER pHeader;
1672 CACHEFILE_ENTRY * pEntry;
1673 URL_CACHEFILE_ENTRY * pUrlEntry;
1674 URLCACHECONTAINER * pContainer;
1676 TRACE("(%s, %p, %p, 0x%08x)\n",
1677 debugstr_a(lpszUrlName),
1678 lpCacheEntryInfo,
1679 lpdwCacheEntryInfoBufferSize,
1680 dwReserved);
1682 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1683 return FALSE;
1685 if (!URLCacheContainer_OpenIndex(pContainer))
1686 return FALSE;
1688 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1689 return FALSE;
1691 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1693 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1694 TRACE("entry %s not found!\n", lpszUrlName);
1695 SetLastError(ERROR_FILE_NOT_FOUND);
1696 return FALSE;
1699 if (pEntry->dwSignature != URL_SIGNATURE)
1701 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1702 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1703 SetLastError(ERROR_FILE_NOT_FOUND);
1704 return FALSE;
1707 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1708 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1709 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1711 pUrlEntry->dwHitRate++;
1712 pUrlEntry->dwUseCount++;
1713 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1715 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1717 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1718 return FALSE;
1720 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1722 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1724 return TRUE;
1727 /***********************************************************************
1728 * RetrieveUrlCacheEntryFileW (WININET.@)
1731 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1732 IN LPCWSTR lpszUrlName,
1733 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1734 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1735 IN DWORD dwReserved
1738 TRACE("(%s, %p, %p, 0x%08x)\n",
1739 debugstr_w(lpszUrlName),
1740 lpCacheEntryInfo,
1741 lpdwCacheEntryInfoBufferSize,
1742 dwReserved);
1744 return FALSE;
1747 /***********************************************************************
1748 * UnlockUrlCacheEntryFileA (WININET.@)
1751 BOOL WINAPI UnlockUrlCacheEntryFileA(
1752 IN LPCSTR lpszUrlName,
1753 IN DWORD dwReserved
1756 LPURLCACHE_HEADER pHeader;
1757 CACHEFILE_ENTRY * pEntry;
1758 URL_CACHEFILE_ENTRY * pUrlEntry;
1759 URLCACHECONTAINER * pContainer;
1761 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1763 if (dwReserved)
1765 ERR("dwReserved != 0\n");
1766 SetLastError(ERROR_INVALID_PARAMETER);
1767 return FALSE;
1770 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1771 return FALSE;
1773 if (!URLCacheContainer_OpenIndex(pContainer))
1774 return FALSE;
1776 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1777 return FALSE;
1779 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1781 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1782 TRACE("entry %s not found!\n", lpszUrlName);
1783 SetLastError(ERROR_FILE_NOT_FOUND);
1784 return FALSE;
1787 if (pEntry->dwSignature != URL_SIGNATURE)
1789 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1790 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1791 SetLastError(ERROR_FILE_NOT_FOUND);
1792 return FALSE;
1795 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1797 if (pUrlEntry->dwUseCount == 0)
1799 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1800 return FALSE;
1802 pUrlEntry->dwUseCount--;
1803 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1805 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1807 return TRUE;
1810 /***********************************************************************
1811 * UnlockUrlCacheEntryFileW (WININET.@)
1814 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1816 FIXME("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1817 return TRUE;
1820 /***********************************************************************
1821 * CreateUrlCacheEntryA (WININET.@)
1824 BOOL WINAPI CreateUrlCacheEntryA(
1825 IN LPCSTR lpszUrlName,
1826 IN DWORD dwExpectedFileSize,
1827 IN LPCSTR lpszFileExtension,
1828 OUT LPSTR lpszFileName,
1829 IN DWORD dwReserved
1832 DWORD len;
1833 WCHAR *url_name;
1834 WCHAR *file_extension;
1835 WCHAR file_name[MAX_PATH];
1836 BOOL bSuccess = FALSE;
1837 DWORD dwError = 0;
1839 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
1840 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1842 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
1843 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
1844 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1846 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
1847 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
1849 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
1851 bSuccess = TRUE;
1853 else
1855 dwError = GetLastError();
1858 else
1860 dwError = GetLastError();
1862 HeapFree(GetProcessHeap(), 0, file_extension);
1864 else
1866 dwError = GetLastError();
1868 HeapFree(GetProcessHeap(), 0, url_name);
1869 if (!bSuccess)
1870 SetLastError(dwError);
1872 return bSuccess;
1874 /***********************************************************************
1875 * CreateUrlCacheEntryW (WININET.@)
1878 BOOL WINAPI CreateUrlCacheEntryW(
1879 IN LPCWSTR lpszUrlName,
1880 IN DWORD dwExpectedFileSize,
1881 IN LPCWSTR lpszFileExtension,
1882 OUT LPWSTR lpszFileName,
1883 IN DWORD dwReserved
1886 URLCACHECONTAINER * pContainer;
1887 LPURLCACHE_HEADER pHeader;
1888 CHAR szFile[MAX_PATH];
1889 WCHAR szExtension[MAX_PATH];
1890 LPCWSTR lpszUrlPart;
1891 LPCWSTR lpszUrlEnd;
1892 LPCWSTR lpszFileNameExtension;
1893 LPWSTR lpszFileNameNoPath;
1894 int i;
1895 int countnoextension;
1896 BYTE CacheDir;
1897 LONG lBufferSize;
1898 BOOL bFound = FALSE;
1899 int count;
1900 static const WCHAR szWWW[] = {'w','w','w',0};
1902 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
1903 debugstr_w(lpszUrlName),
1904 dwExpectedFileSize,
1905 debugstr_w(lpszFileExtension),
1906 lpszFileName,
1907 dwReserved);
1909 if (dwReserved)
1911 ERR("dwReserved != 0\n");
1912 SetLastError(ERROR_INVALID_PARAMETER);
1913 return FALSE;
1916 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1919 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1920 lpszUrlEnd--;
1922 for (lpszUrlPart = lpszUrlEnd;
1923 (lpszUrlPart >= lpszUrlName);
1924 lpszUrlPart--)
1926 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1928 bFound = TRUE;
1929 lpszUrlPart++;
1930 break;
1933 if (!lstrcmpW(lpszUrlPart, szWWW))
1935 lpszUrlPart += lstrlenW(szWWW);
1938 count = lpszUrlEnd - lpszUrlPart;
1940 if (bFound && (count < MAX_PATH))
1942 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
1943 if (!len)
1944 return FALSE;
1945 szFile[len] = '\0';
1946 /* FIXME: get rid of illegal characters like \, / and : */
1948 else
1950 FIXME("need to generate a random filename\n");
1953 TRACE("File name: %s\n", debugstr_a(szFile));
1955 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1956 return FALSE;
1958 if (!URLCacheContainer_OpenIndex(pContainer))
1959 return FALSE;
1961 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1962 return FALSE;
1964 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1966 lBufferSize = MAX_PATH * sizeof(WCHAR);
1967 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1969 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1971 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
1972 lpszFileNameNoPath >= lpszFileName;
1973 --lpszFileNameNoPath)
1975 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
1976 break;
1979 countnoextension = lstrlenW(lpszFileNameNoPath);
1980 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
1981 if (lpszFileNameExtension)
1982 countnoextension -= lstrlenW(lpszFileNameExtension);
1983 *szExtension = '\0';
1985 if (lpszFileExtension)
1987 szExtension[0] = '.';
1988 lstrcpyW(szExtension+1, lpszFileExtension);
1991 for (i = 0; i < 255; i++)
1993 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
1994 HANDLE hFile;
1995 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
1996 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
1997 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1998 if (hFile != INVALID_HANDLE_VALUE)
2000 CloseHandle(hFile);
2001 return TRUE;
2005 return FALSE;
2009 /***********************************************************************
2010 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2012 * The bug we are compensating for is that some drongo at Microsoft
2013 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2014 * As a consequence, CommitUrlCacheEntryA has been effectively
2015 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2016 * is still defined as LPCWSTR. The result (other than madness) is
2017 * that we always need to store lpHeaderInfo in CP_ACP rather than
2018 * in UTF16, and we need to avoid converting lpHeaderInfo in
2019 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2020 * result will lose data for arbitrary binary data.
2023 static BOOL WINAPI CommitUrlCacheEntryInternal(
2024 IN LPCWSTR lpszUrlName,
2025 IN LPCWSTR lpszLocalFileName,
2026 IN FILETIME ExpireTime,
2027 IN FILETIME LastModifiedTime,
2028 IN DWORD CacheEntryType,
2029 IN LPBYTE lpHeaderInfo,
2030 IN DWORD dwHeaderSize,
2031 IN LPCWSTR lpszFileExtension,
2032 IN LPCWSTR lpszOriginalUrl
2035 URLCACHECONTAINER * pContainer;
2036 LPURLCACHE_HEADER pHeader;
2037 CACHEFILE_ENTRY * pEntry;
2038 URL_CACHEFILE_ENTRY * pUrlEntry;
2039 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2040 DWORD dwOffsetLocalFileName = 0;
2041 DWORD dwOffsetHeader = 0;
2042 DWORD dwFileSizeLow = 0;
2043 DWORD dwFileSizeHigh = 0;
2044 BYTE cDirectory = 0;
2045 char achFile[MAX_PATH];
2046 char achUrl[MAX_PATH];
2047 char *pchLocalFileName = 0;
2049 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2050 debugstr_w(lpszUrlName),
2051 debugstr_w(lpszLocalFileName),
2052 CacheEntryType,
2053 lpHeaderInfo,
2054 dwHeaderSize,
2055 debugstr_w(lpszFileExtension),
2056 debugstr_w(lpszOriginalUrl));
2058 if (lpszOriginalUrl)
2059 WARN(": lpszOriginalUrl ignored\n");
2061 if (lpszLocalFileName)
2063 HANDLE hFile;
2065 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2066 if (hFile == INVALID_HANDLE_VALUE)
2068 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2069 return FALSE;
2072 /* Get file size */
2073 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2074 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2076 ERR("couldn't get file size (error is %d)\n", GetLastError());
2077 CloseHandle(hFile);
2078 return FALSE;
2081 CloseHandle(hFile);
2084 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2085 return FALSE;
2087 if (!URLCacheContainer_OpenIndex(pContainer))
2088 return FALSE;
2090 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2091 return FALSE;
2093 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
2095 if (URLCache_FindEntryInHash(pHeader, achUrl, &pEntry))
2097 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2098 FIXME("entry already in cache - don't know what to do!\n");
2100 * SetLastError(ERROR_FILE_NOT_FOUND);
2101 * return FALSE;
2103 return TRUE;
2106 if (lpszLocalFileName)
2108 BOOL bFound = FALSE;
2110 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2112 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2113 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2114 SetLastError(ERROR_INVALID_PARAMETER);
2115 return FALSE;
2118 /* skip container path prefix */
2119 lpszLocalFileName += lstrlenW(pContainer->path);
2121 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2122 pchLocalFileName = achFile;
2124 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2126 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2128 bFound = TRUE;
2129 break;
2133 if (!bFound)
2135 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2136 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2137 SetLastError(ERROR_INVALID_PARAMETER);
2138 return FALSE;
2141 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2144 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
2145 if (lpszLocalFileName)
2147 dwOffsetLocalFileName = dwBytesNeeded;
2148 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2150 if (lpHeaderInfo)
2152 dwOffsetHeader = dwBytesNeeded;
2153 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2156 /* round up to next block */
2157 if (dwBytesNeeded % BLOCKSIZE)
2159 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2160 dwBytesNeeded += BLOCKSIZE;
2163 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2165 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2166 ERR("no free entries\n");
2167 SetLastError(ERROR_DISK_FULL);
2168 return FALSE;
2171 /* FindFirstFreeEntry fills in blocks used */
2172 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2173 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2174 pUrlEntry->CacheDir = cDirectory;
2175 pUrlEntry->CacheEntryType = CacheEntryType;
2176 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2177 pUrlEntry->dwExemptDelta = 0;
2178 pUrlEntry->dwHitRate = 0;
2179 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2180 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2181 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2182 pUrlEntry->dwSizeHigh = 0;
2183 pUrlEntry->dwSizeLow = dwFileSizeLow;
2184 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2185 pUrlEntry->dwUseCount = 0;
2186 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2187 pUrlEntry->LastModifiedTime = LastModifiedTime;
2188 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2189 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2190 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2191 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2193 /*** Unknowns ***/
2194 pUrlEntry->dwUnknown1 = 0;
2195 pUrlEntry->dwUnknown2 = 0;
2196 pUrlEntry->dwUnknown3 = 0x60;
2197 pUrlEntry->Unknown4 = 0;
2198 pUrlEntry->wUnknown5 = 0x1010;
2199 pUrlEntry->dwUnknown6 = 0;
2200 pUrlEntry->dwUnknown7 = 0;
2201 pUrlEntry->dwUnknown8 = 0;
2204 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, achUrl);
2205 if (dwOffsetLocalFileName)
2206 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2207 if (dwOffsetHeader)
2208 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2210 if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2212 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2213 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2214 return FALSE;
2217 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2219 return TRUE;
2222 /***********************************************************************
2223 * CommitUrlCacheEntryA (WININET.@)
2226 BOOL WINAPI CommitUrlCacheEntryA(
2227 IN LPCSTR lpszUrlName,
2228 IN LPCSTR lpszLocalFileName,
2229 IN FILETIME ExpireTime,
2230 IN FILETIME LastModifiedTime,
2231 IN DWORD CacheEntryType,
2232 IN LPBYTE lpHeaderInfo,
2233 IN DWORD dwHeaderSize,
2234 IN LPCSTR lpszFileExtension,
2235 IN LPCSTR lpszOriginalUrl
2238 DWORD len;
2239 WCHAR *url_name;
2240 WCHAR *local_file_name;
2241 WCHAR *original_url = NULL;
2242 BOOL bSuccess = FALSE;
2243 DWORD dwError = 0;
2245 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2246 debugstr_a(lpszUrlName),
2247 debugstr_a(lpszLocalFileName),
2248 CacheEntryType,
2249 lpHeaderInfo,
2250 dwHeaderSize,
2251 debugstr_a(lpszFileExtension),
2252 debugstr_a(lpszOriginalUrl));
2254 if (lpszFileExtension != 0)
2256 SetLastError(ERROR_INVALID_PARAMETER);
2257 return FALSE;
2259 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2260 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2262 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2263 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2264 (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2266 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2267 if (!lpszOriginalUrl ||
2268 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2269 (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2271 if (original_url)
2272 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2273 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2274 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2275 NULL, original_url))
2277 bSuccess = TRUE;
2279 else
2281 dwError = GetLastError();
2283 HeapFree(GetProcessHeap(), 0, original_url);
2285 else
2287 dwError = GetLastError();
2289 HeapFree(GetProcessHeap(), 0, local_file_name);
2291 else
2293 dwError = GetLastError();
2295 HeapFree(GetProcessHeap(), 0, url_name);
2296 if (!bSuccess)
2297 SetLastError(dwError);
2299 return bSuccess;
2302 /***********************************************************************
2303 * CommitUrlCacheEntryW (WININET.@)
2306 BOOL WINAPI CommitUrlCacheEntryW(
2307 IN LPCWSTR lpszUrlName,
2308 IN LPCWSTR lpszLocalFileName,
2309 IN FILETIME ExpireTime,
2310 IN FILETIME LastModifiedTime,
2311 IN DWORD CacheEntryType,
2312 IN LPWSTR lpHeaderInfo,
2313 IN DWORD dwHeaderSize,
2314 IN LPCWSTR lpszFileExtension,
2315 IN LPCWSTR lpszOriginalUrl
2318 DWORD dwError = 0;
2319 BOOL bSuccess = FALSE;
2320 DWORD len = 0;
2321 CHAR *header_info = NULL;
2323 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2324 debugstr_w(lpszUrlName),
2325 debugstr_w(lpszLocalFileName),
2326 CacheEntryType,
2327 lpHeaderInfo,
2328 dwHeaderSize,
2329 debugstr_w(lpszFileExtension),
2330 debugstr_w(lpszOriginalUrl));
2332 if (!lpHeaderInfo ||
2333 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2334 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2336 if (header_info)
2337 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2338 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2339 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2341 bSuccess = TRUE;
2343 else
2345 dwError = GetLastError();
2347 if (header_info)
2349 HeapFree(GetProcessHeap(), 0, header_info);
2350 if (!bSuccess)
2351 SetLastError(dwError);
2354 return bSuccess;
2357 /***********************************************************************
2358 * ReadUrlCacheEntryStream (WININET.@)
2361 BOOL WINAPI ReadUrlCacheEntryStream(
2362 IN HANDLE hUrlCacheStream,
2363 IN DWORD dwLocation,
2364 IN OUT LPVOID lpBuffer,
2365 IN OUT LPDWORD lpdwLen,
2366 IN DWORD dwReserved
2369 /* Get handle to file from 'stream' */
2370 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2372 if (dwReserved != 0)
2374 ERR("dwReserved != 0\n");
2375 SetLastError(ERROR_INVALID_PARAMETER);
2376 return FALSE;
2379 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2381 SetLastError(ERROR_INVALID_HANDLE);
2382 return FALSE;
2385 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2386 return FALSE;
2387 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2390 /***********************************************************************
2391 * RetrieveUrlCacheEntryStreamA (WININET.@)
2394 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2395 IN LPCSTR lpszUrlName,
2396 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2397 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2398 IN BOOL fRandomRead,
2399 IN DWORD dwReserved
2402 /* NOTE: this is not the same as the way that the native
2403 * version allocates 'stream' handles. I did it this way
2404 * as it is much easier and no applications should depend
2405 * on this behaviour. (Native version appears to allocate
2406 * indices into a table)
2408 STREAM_HANDLE * pStream;
2409 HANDLE hFile;
2411 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2412 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2414 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2415 lpCacheEntryInfo,
2416 lpdwCacheEntryInfoBufferSize,
2417 dwReserved))
2419 return NULL;
2422 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2423 GENERIC_READ,
2424 FILE_SHARE_READ,
2425 NULL,
2426 OPEN_EXISTING,
2427 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2428 NULL);
2429 if (hFile == INVALID_HANDLE_VALUE)
2430 return FALSE;
2432 /* allocate handle storage space */
2433 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2434 if (!pStream)
2436 CloseHandle(hFile);
2437 SetLastError(ERROR_OUTOFMEMORY);
2438 return FALSE;
2441 pStream->hFile = hFile;
2442 strcpy(pStream->lpszUrl, lpszUrlName);
2443 return (HANDLE)pStream;
2446 /***********************************************************************
2447 * RetrieveUrlCacheEntryStreamW (WININET.@)
2450 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2451 IN LPCWSTR lpszUrlName,
2452 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2453 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2454 IN BOOL fRandomRead,
2455 IN DWORD dwReserved
2458 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2459 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2460 return NULL;
2463 /***********************************************************************
2464 * UnlockUrlCacheEntryStream (WININET.@)
2467 BOOL WINAPI UnlockUrlCacheEntryStream(
2468 IN HANDLE hUrlCacheStream,
2469 IN DWORD dwReserved
2472 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2474 if (dwReserved != 0)
2476 ERR("dwReserved != 0\n");
2477 SetLastError(ERROR_INVALID_PARAMETER);
2478 return FALSE;
2481 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2483 SetLastError(ERROR_INVALID_HANDLE);
2484 return FALSE;
2487 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2488 return FALSE;
2490 /* close file handle */
2491 CloseHandle(pStream->hFile);
2493 /* free allocated space */
2494 HeapFree(GetProcessHeap(), 0, pStream);
2496 return TRUE;
2500 /***********************************************************************
2501 * DeleteUrlCacheEntryA (WININET.@)
2504 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2506 URLCACHECONTAINER * pContainer;
2507 LPURLCACHE_HEADER pHeader;
2508 CACHEFILE_ENTRY * pEntry;
2510 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2512 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2513 return FALSE;
2515 if (!URLCacheContainer_OpenIndex(pContainer))
2516 return FALSE;
2518 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2519 return FALSE;
2521 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2523 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2524 TRACE("entry %s not found!\n", lpszUrlName);
2525 SetLastError(ERROR_FILE_NOT_FOUND);
2526 return FALSE;
2529 URLCache_DeleteEntry(pHeader, pEntry);
2531 URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2533 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2535 return TRUE;
2538 /***********************************************************************
2539 * DeleteUrlCacheEntryW (WININET.@)
2542 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2544 FIXME("(%s) stub\n", debugstr_w(lpszUrlName));
2545 return TRUE;
2548 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2550 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2551 return TRUE;
2554 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2556 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2557 return TRUE;
2560 /***********************************************************************
2561 * CreateCacheContainerA (WININET.@)
2563 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2564 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2566 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2567 d1, d2, d3, d4, d5, d6, d7, d8);
2568 return TRUE;
2571 /***********************************************************************
2572 * CreateCacheContainerW (WININET.@)
2574 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2575 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2577 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2578 d1, d2, d3, d4, d5, d6, d7, d8);
2579 return TRUE;
2582 /***********************************************************************
2583 * FindCloseUrlCache (WININET.@)
2585 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2587 FIXME("(%p) stub\n", hEnumHandle);
2588 return TRUE;
2591 /***********************************************************************
2592 * FindFirstUrlCacheContainerA (WININET.@)
2594 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2596 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2597 return NULL;
2600 /***********************************************************************
2601 * FindFirstUrlCacheContainerW (WININET.@)
2603 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2605 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2606 return NULL;
2609 /***********************************************************************
2610 * FindNextUrlCacheContainerA (WININET.@)
2612 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2614 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2615 return FALSE;
2618 /***********************************************************************
2619 * FindNextUrlCacheContainerW (WININET.@)
2621 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2623 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2624 return FALSE;
2627 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2628 LPCSTR lpszUrlSearchPattern,
2629 DWORD dwFlags,
2630 DWORD dwFilter,
2631 GROUPID GroupId,
2632 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2633 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2634 LPVOID lpReserved,
2635 LPDWORD pcbReserved2,
2636 LPVOID lpReserved3
2639 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2640 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2641 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2642 SetLastError(ERROR_FILE_NOT_FOUND);
2643 return NULL;
2646 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2647 LPCWSTR lpszUrlSearchPattern,
2648 DWORD dwFlags,
2649 DWORD dwFilter,
2650 GROUPID GroupId,
2651 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2652 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2653 LPVOID lpReserved,
2654 LPDWORD pcbReserved2,
2655 LPVOID lpReserved3
2658 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2659 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2660 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2661 SetLastError(ERROR_FILE_NOT_FOUND);
2662 return NULL;
2665 /***********************************************************************
2666 * FindFirstUrlCacheEntryA (WININET.@)
2669 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2670 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2672 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2673 SetLastError(ERROR_FILE_NOT_FOUND);
2674 return 0;
2677 /***********************************************************************
2678 * FindFirstUrlCacheEntryW (WININET.@)
2681 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2682 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2684 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2685 return 0;
2688 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2689 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2691 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2692 dwSearchCondition, lpGroupId, lpReserved);
2693 return NULL;
2696 BOOL WINAPI FindNextUrlCacheEntryA(
2697 HANDLE hEnumHandle,
2698 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2699 LPDWORD lpdwNextCacheEntryInfoBufferSize
2702 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2703 return FALSE;
2706 BOOL WINAPI FindNextUrlCacheEntryW(
2707 HANDLE hEnumHandle,
2708 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2709 LPDWORD lpdwNextCacheEntryInfoBufferSize
2712 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2713 return FALSE;
2716 BOOL WINAPI FindNextUrlCacheEntryExA(
2717 HANDLE hEnumHandle,
2718 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2719 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2720 LPVOID lpReserved,
2721 LPDWORD pcbReserved2,
2722 LPVOID lpReserved3
2725 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2726 lpReserved, pcbReserved2, lpReserved3);
2727 return FALSE;
2730 BOOL WINAPI FindNextUrlCacheEntryExW(
2731 HANDLE hEnumHandle,
2732 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2733 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2734 LPVOID lpReserved,
2735 LPDWORD pcbReserved2,
2736 LPVOID lpReserved3
2739 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2740 lpReserved, pcbReserved2, lpReserved3);
2741 return FALSE;
2744 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2746 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2747 return FALSE;
2750 /***********************************************************************
2751 * CreateUrlCacheGroup (WININET.@)
2754 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2756 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
2757 return FALSE;
2760 /***********************************************************************
2761 * DeleteUrlCacheGroup (WININET.@)
2764 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2766 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
2767 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2768 return FALSE;
2771 /***********************************************************************
2772 * SetUrlCacheEntryGroupA (WININET.@)
2775 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2776 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2777 LPVOID lpReserved)
2779 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2780 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2781 pbGroupAttributes, cbGroupAttributes, lpReserved);
2782 SetLastError(ERROR_FILE_NOT_FOUND);
2783 return FALSE;
2786 /***********************************************************************
2787 * SetUrlCacheEntryGroupW (WININET.@)
2790 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2791 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2792 LPVOID lpReserved)
2794 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2795 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2796 pbGroupAttributes, cbGroupAttributes, lpReserved);
2797 SetLastError(ERROR_FILE_NOT_FOUND);
2798 return FALSE;
2801 /***********************************************************************
2802 * GetUrlCacheConfigInfoW (WININET.@)
2804 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2806 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2807 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2808 return FALSE;
2811 /***********************************************************************
2812 * GetUrlCacheConfigInfoA (WININET.@)
2814 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2816 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2818 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2819 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2820 return FALSE;
2823 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2824 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2825 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2827 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2828 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2829 lpdwGroupInfo, lpReserved);
2830 return FALSE;
2833 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2834 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2835 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2837 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2838 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2839 lpdwGroupInfo, lpReserved);
2840 return FALSE;
2843 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2844 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2846 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2847 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2848 return TRUE;
2851 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2852 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2854 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2855 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2856 return TRUE;
2859 BOOL WINAPI SetUrlCacheConfigInfoA( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2861 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2862 return TRUE;
2865 BOOL WINAPI SetUrlCacheConfigInfoW( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2867 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2868 return TRUE;
2871 /***********************************************************************
2872 * DeleteIE3Cache (WININET.@)
2874 * Deletes the files used by the IE3 URL caching system.
2876 * PARAMS
2877 * hWnd [I] A dummy window.
2878 * hInst [I] Instance of process calling the function.
2879 * lpszCmdLine [I] Options used by function.
2880 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
2882 * RETURNS
2883 * nothing
2885 void WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2887 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);