libwine: Set install name to an absolute path for the installed library on Mac OS.
[wine/wine64.git] / dlls / wininet / urlcache.c
blob42f3ba9c600f93f0e5552bb535235e8e881950a7
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 "shlobj.h"
47 #include "wine/unicode.h"
48 #include "wine/list.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
53 #define ENTRY_START_OFFSET 0x4000
54 #define DIR_LENGTH 8
55 #define BLOCKSIZE 128
56 #define HASHTABLE_SIZE 448
57 #define HASHTABLE_BLOCKSIZE 7
58 #define HASHTABLE_FREE 3
59 #define ALLOCATION_TABLE_OFFSET 0x250
60 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
61 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
62 #define NEWFILE_NUM_BLOCKS 0xd80
63 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
65 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
66 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
67 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
68 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
69 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
71 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
73 typedef struct _CACHEFILE_ENTRY
75 /* union
76 {*/
77 DWORD dwSignature; /* e.g. "URL " */
78 /* CHAR szSignature[4];
79 };*/
80 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
81 } CACHEFILE_ENTRY;
83 typedef struct _URL_CACHEFILE_ENTRY
85 CACHEFILE_ENTRY CacheFileEntry;
86 FILETIME LastModifiedTime;
87 FILETIME LastAccessTime;
88 WORD wExpiredDate; /* expire date in dos format */
89 WORD wExpiredTime; /* expire time in dos format */
90 DWORD dwUnknown1; /* usually zero */
91 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
92 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
93 DWORD dwUnknown2; /* usually zero */
94 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
95 DWORD dwUnknown3; /* usually 0x60 */
96 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
97 BYTE CacheDir; /* index of cache directory this url is stored in */
98 BYTE Unknown4; /* usually zero */
99 WORD wUnknown5; /* usually 0x1010 */
100 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
101 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
102 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
103 DWORD dwHeaderInfoSize;
104 DWORD dwUnknown6; /* usually zero */
105 WORD wLastSyncDate; /* last sync date in dos format */
106 WORD wLastSyncTime; /* last sync time in dos format */
107 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
108 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109 WORD wUnknownDate; /* usually same as wLastSyncDate */
110 WORD wUnknownTime; /* usually same as wLastSyncTime */
111 DWORD dwUnknown7; /* usually zero */
112 DWORD dwUnknown8; /* usually zero */
113 /* packing to dword align start of next field */
114 /* CHAR szSourceUrlName[]; (url) */
115 /* packing to dword align start of next field */
116 /* CHAR szLocalFileName[]; (local file name exluding path) */
117 /* packing to dword align start of next field */
118 /* CHAR szHeaderInfo[]; (header info) */
119 } URL_CACHEFILE_ENTRY;
121 struct _HASH_ENTRY
123 DWORD dwHashKey;
124 DWORD dwOffsetEntry;
127 typedef struct _HASH_CACHEFILE_ENTRY
129 CACHEFILE_ENTRY CacheFileEntry;
130 DWORD dwAddressNext;
131 DWORD dwHashTableNumber;
132 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
133 } HASH_CACHEFILE_ENTRY;
135 typedef struct _DIRECTORY_DATA
137 DWORD dwUnknown;
138 char filename[DIR_LENGTH];
139 } DIRECTORY_DATA;
141 typedef struct _URLCACHE_HEADER
143 char szSignature[28];
144 DWORD dwFileSize;
145 DWORD dwOffsetFirstHashTable;
146 DWORD dwIndexCapacityInBlocks;
147 DWORD dwBlocksInUse;
148 DWORD dwUnknown1;
149 DWORD dwCacheLimitLow; /* disk space limit for cache */
150 DWORD dwCacheLimitHigh; /* disk space limit for cache */
151 DWORD dwUnknown4; /* current disk space usage for cache */
152 DWORD dwUnknown5; /* current disk space usage for cache */
153 DWORD dwUnknown6; /* possibly a flag? */
154 DWORD dwUnknown7;
155 BYTE DirectoryCount; /* number of directory_data's */
156 BYTE Unknown8[3]; /* just padding? */
157 DIRECTORY_DATA directory_data[1]; /* first directory entry */
158 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
159 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
161 typedef struct _STREAM_HANDLE
163 HANDLE hFile;
164 CHAR lpszUrl[1];
165 } STREAM_HANDLE;
167 typedef struct _URLCACHECONTAINER
169 struct list entry; /* part of a list */
170 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
171 LPWSTR path; /* path to url container directory */
172 HANDLE hMapping; /* handle of file mapping */
173 DWORD file_size; /* size of file when mapping was opened */
174 HANDLE hMutex; /* hande of mutex */
175 } URLCACHECONTAINER;
178 /* List of all containers available */
179 static struct list UrlContainers = LIST_INIT(UrlContainers);
181 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash);
183 /***********************************************************************
184 * URLCache_PathToObjectName (Internal)
186 * Converts a path to a name suitable for use as a Win32 object name.
187 * Replaces '\\' characters in-place with the specified character
188 * (usually '_' or '!')
190 * RETURNS
191 * nothing
194 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
196 for (; *lpszPath; lpszPath++)
198 if (*lpszPath == '\\')
199 *lpszPath = replace;
203 /***********************************************************************
204 * URLCacheContainer_OpenIndex (Internal)
206 * Opens the index file and saves mapping handle in hCacheIndexMapping
208 * RETURNS
209 * TRUE if succeeded
210 * FALSE if failed
213 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
215 HANDLE hFile;
216 WCHAR wszFilePath[MAX_PATH];
217 DWORD dwFileSize;
219 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
220 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
222 if (pContainer->hMapping)
223 return TRUE;
225 strcpyW(wszFilePath, pContainer->path);
226 strcatW(wszFilePath, wszIndex);
228 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
229 if (hFile == INVALID_HANDLE_VALUE)
231 /* Maybe the directory wasn't there? Try to create it */
232 if (CreateDirectoryW(pContainer->path, 0))
233 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
235 if (hFile == INVALID_HANDLE_VALUE)
237 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
238 return FALSE;
241 /* At this stage we need the mutex because we may be about to create the
242 * file.
244 WaitForSingleObject(pContainer->hMutex, INFINITE);
246 dwFileSize = GetFileSize(hFile, NULL);
247 if (dwFileSize == INVALID_FILE_SIZE)
249 ReleaseMutex(pContainer->hMutex);
250 return FALSE;
253 if (dwFileSize == 0)
255 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
256 HKEY key;
257 char achZeroes[0x1000];
258 DWORD dwOffset;
259 DWORD dwError = 0;
261 /* Write zeroes to the entire file so we can safely map it without
262 * fear of getting a SEGV because the disk is full.
264 memset(achZeroes, 0, sizeof(achZeroes));
265 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
267 DWORD dwWrite = sizeof(achZeroes);
268 DWORD dwWritten;
270 if (NEWFILE_SIZE - dwOffset < dwWrite)
271 dwWrite = NEWFILE_SIZE - dwOffset;
272 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
273 dwWritten != dwWrite)
275 /* If we fail to write, we need to return the error that
276 * cause the problem and also make sure the file is no
277 * longer there, if possible.
279 dwError = GetLastError();
281 break;
285 if (!dwError)
287 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
289 if (hMapping)
291 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
293 if (pHeader)
295 WCHAR *pwchDir;
296 WCHAR wszDirPath[MAX_PATH];
297 FILETIME ft;
298 int i, j;
300 dwFileSize = NEWFILE_SIZE;
302 /* First set some constants and defaults in the header */
303 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
304 pHeader->dwFileSize = dwFileSize;
305 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
306 /* 127MB - taken from default for Windows 2000 */
307 pHeader->dwCacheLimitHigh = 0;
308 pHeader->dwCacheLimitLow = 0x07ff5400;
309 /* Copied from a Windows 2000 cache index */
310 pHeader->DirectoryCount = 4;
312 /* If the registry has a cache size set, use the registry value */
313 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
315 DWORD dw;
316 DWORD len = sizeof(dw);
317 DWORD keytype;
319 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
320 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
321 keytype == REG_DWORD)
323 pHeader->dwCacheLimitHigh = (dw >> 22);
324 pHeader->dwCacheLimitLow = dw << 10;
326 RegCloseKey(key);
329 URLCache_CreateHashTable(pHeader, NULL);
331 /* Last step - create the directories */
333 strcpyW(wszDirPath, pContainer->path);
334 pwchDir = wszDirPath + strlenW(wszDirPath);
335 pwchDir[8] = 0;
337 GetSystemTimeAsFileTime(&ft);
339 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
341 /* The following values were copied from a Windows index.
342 * I don't know what the values are supposed to mean but
343 * have made them the same in the hope that this will
344 * be better for compatibility
346 pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
347 for (j = 0;; ++j)
349 int k;
350 ULONGLONG n = ft.dwHighDateTime;
352 /* Generate a file name to attempt to create.
353 * This algorithm will create what will appear
354 * to be random and unrelated directory names
355 * of up to 9 characters in length.
357 n <<= 32;
358 n += ft.dwLowDateTime;
359 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
361 for (k = 0; k < 8; ++k)
363 int r = (n % 36);
365 /* Dividing by a prime greater than 36 helps
366 * with the appearance of randomness
368 n /= 37;
370 if (r < 10)
371 pwchDir[k] = '0' + r;
372 else
373 pwchDir[k] = 'A' + (r - 10);
376 if (CreateDirectoryW(wszDirPath, 0))
378 int k;
380 /* The following is OK because we generated an
381 * 8 character directory name made from characters
382 * [A-Z0-9], which are equivalent for all code
383 * pages and for UTF-16
385 for (k = 0; k < 8; ++k)
386 pHeader->directory_data[i].filename[k] = pwchDir[k];
387 break;
389 else if (j >= 255)
391 /* Give up. The most likely cause of this
392 * is a full disk, but whatever the cause
393 * is, it should be more than apparent that
394 * we won't succeed.
396 dwError = GetLastError();
397 break;
402 UnmapViewOfFile(pHeader);
404 else
406 dwError = GetLastError();
408 CloseHandle(hMapping);
410 else
412 dwError = GetLastError();
416 if (dwError)
418 CloseHandle(hFile);
419 DeleteFileW(wszFilePath);
420 ReleaseMutex(pContainer->hMutex);
421 SetLastError(dwError);
422 return FALSE;
427 ReleaseMutex(pContainer->hMutex);
429 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
430 URLCache_PathToObjectName(wszFilePath, '_');
431 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
432 if (!pContainer->hMapping)
433 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
434 CloseHandle(hFile);
435 if (!pContainer->hMapping)
437 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
438 return FALSE;
441 return TRUE;
444 /***********************************************************************
445 * URLCacheContainer_CloseIndex (Internal)
447 * Closes the index
449 * RETURNS
450 * nothing
453 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
455 CloseHandle(pContainer->hMapping);
456 pContainer->hMapping = NULL;
459 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
461 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
462 int path_len = strlenW(path);
463 int cache_prefix_len = strlenW(cache_prefix);
465 if (!pContainer)
467 return FALSE;
470 pContainer->hMapping = NULL;
471 pContainer->file_size = 0;
473 pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
474 if (!pContainer->path)
476 HeapFree(GetProcessHeap(), 0, pContainer);
477 return FALSE;
480 memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
482 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
483 if (!pContainer->cache_prefix)
485 HeapFree(GetProcessHeap(), 0, pContainer->path);
486 HeapFree(GetProcessHeap(), 0, pContainer);
487 return FALSE;
490 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
492 CharLowerW(mutex_name);
493 URLCache_PathToObjectName(mutex_name, '!');
495 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
497 ERR("couldn't create mutex (error is %d)\n", GetLastError());
498 HeapFree(GetProcessHeap(), 0, pContainer->path);
499 HeapFree(GetProcessHeap(), 0, pContainer);
500 return FALSE;
503 list_add_head(&UrlContainers, &pContainer->entry);
505 return TRUE;
508 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
510 list_remove(&pContainer->entry);
512 URLCacheContainer_CloseIndex(pContainer);
513 CloseHandle(pContainer->hMutex);
514 HeapFree(GetProcessHeap(), 0, pContainer->path);
515 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
516 HeapFree(GetProcessHeap(), 0, pContainer);
519 void URLCacheContainers_CreateDefaults(void)
521 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
522 static const WCHAR UrlPrefix[] = {0};
523 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
524 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
525 static const WCHAR CookieSuffix[] = {0};
526 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
527 static const struct
529 int nFolder; /* CSIDL_* constant */
530 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
531 const WCHAR * cache_prefix; /* prefix used to reference the container */
532 } DefaultContainerData[] =
534 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
535 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
536 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
538 DWORD i;
540 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
542 WCHAR wszCachePath[MAX_PATH];
543 WCHAR wszMutexName[MAX_PATH];
544 int path_len, suffix_len;
546 if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
548 ERR("Couldn't get path for default container %u\n", i);
549 continue;
551 path_len = strlenW(wszCachePath);
552 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
554 if (path_len + suffix_len + 2 > MAX_PATH)
556 ERR("Path too long\n");
557 continue;
560 wszCachePath[path_len] = '\\';
561 wszCachePath[path_len+1] = 0;
563 strcpyW(wszMutexName, wszCachePath);
565 if (suffix_len)
567 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
568 wszCachePath[path_len + suffix_len + 1] = '\\';
569 wszCachePath[path_len + suffix_len + 2] = '\0';
572 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
576 void URLCacheContainers_DeleteAll(void)
578 while(!list_empty(&UrlContainers))
579 URLCacheContainer_DeleteContainer(
580 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
584 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
586 struct list * cursor;
588 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
590 LIST_FOR_EACH(cursor, &UrlContainers)
592 URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
593 int prefix_len = strlenW(pContainer->cache_prefix);
594 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
596 TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
597 *ppContainer = pContainer;
598 return TRUE;
601 ERR("no container found\n");
602 SetLastError(ERROR_FILE_NOT_FOUND);
603 return FALSE;
606 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
608 BOOL ret;
609 LPWSTR lpwszUrl;
610 int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
611 if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
613 MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
614 ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
615 HeapFree(GetProcessHeap(), 0, lpwszUrl);
616 return ret;
618 return FALSE;
621 /***********************************************************************
622 * URLCacheContainer_LockIndex (Internal)
625 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
627 BYTE index;
628 LPVOID pIndexData;
629 URLCACHE_HEADER * pHeader;
631 /* acquire mutex */
632 WaitForSingleObject(pContainer->hMutex, INFINITE);
634 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
636 if (!pIndexData)
638 ReleaseMutex(pContainer->hMutex);
639 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
640 return FALSE;
642 pHeader = (URLCACHE_HEADER *)pIndexData;
644 /* file has grown - we need to remap to prevent us getting
645 * access violations when we try and access beyond the end
646 * of the memory mapped file */
647 if (pHeader->dwFileSize != pContainer->file_size)
649 URLCacheContainer_CloseIndex(pContainer);
650 if (!URLCacheContainer_OpenIndex(pContainer))
652 ReleaseMutex(pContainer->hMutex);
653 return FALSE;
655 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
657 if (!pIndexData)
659 ReleaseMutex(pContainer->hMutex);
660 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
661 return FALSE;
663 pHeader = (URLCACHE_HEADER *)pIndexData;
666 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
668 for (index = 0; index < pHeader->DirectoryCount; index++)
670 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
673 return pHeader;
676 /***********************************************************************
677 * URLCacheContainer_UnlockIndex (Internal)
680 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
682 /* release mutex */
683 ReleaseMutex(pContainer->hMutex);
684 return UnmapViewOfFile(pHeader);
688 #ifndef CHAR_BIT
689 #define CHAR_BIT (8 * sizeof(CHAR))
690 #endif
692 /***********************************************************************
693 * URLCache_Allocation_BlockIsFree (Internal)
695 * Is the specified block number free?
697 * RETURNS
698 * zero if free
699 * non-zero otherwise
702 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
704 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
705 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
708 /***********************************************************************
709 * URLCache_Allocation_BlockFree (Internal)
711 * Marks the specified block as free
713 * RETURNS
714 * nothing
717 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
719 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
720 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
723 /***********************************************************************
724 * URLCache_Allocation_BlockAllocate (Internal)
726 * Marks the specified block as allocated
728 * RETURNS
729 * nothing
732 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
734 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
735 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
738 /***********************************************************************
739 * URLCache_FindFirstFreeEntry (Internal)
741 * Finds and allocates the first block of free space big enough and
742 * sets ppEntry to point to it.
744 * RETURNS
745 * TRUE if it had enough space
746 * FALSE if it couldn't find enough space
749 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
751 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
752 DWORD dwBlockNumber;
753 DWORD dwFreeCounter;
754 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
756 for (dwFreeCounter = 0;
757 dwFreeCounter < dwBlocksNeeded &&
758 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
759 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
760 dwFreeCounter++)
761 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
763 if (dwFreeCounter == dwBlocksNeeded)
765 DWORD index;
766 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
767 for (index = 0; index < dwBlocksNeeded; index++)
768 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
769 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
770 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
771 return TRUE;
774 FIXME("Grow file\n");
775 return FALSE;
778 /***********************************************************************
779 * URLCache_DeleteEntry (Internal)
781 * Deletes the specified entry and frees the space allocated to it
783 * RETURNS
784 * TRUE if it succeeded
785 * FALSE if it failed
788 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
790 DWORD dwStartBlock;
791 DWORD dwBlock;
792 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
794 /* update allocation table */
795 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
796 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
797 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
799 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
800 return TRUE;
803 /***********************************************************************
804 * URLCache_LocalFileNameToPathW (Internal)
806 * Copies the full path to the specified buffer given the local file
807 * name and the index of the directory it is in. Always sets value in
808 * lpBufferSize to the required buffer size (in bytes).
810 * RETURNS
811 * TRUE if the buffer was big enough
812 * FALSE if the buffer was too small
815 static BOOL URLCache_LocalFileNameToPathW(
816 const URLCACHECONTAINER * pContainer,
817 LPCURLCACHE_HEADER pHeader,
818 LPCSTR szLocalFileName,
819 BYTE Directory,
820 LPWSTR wszPath,
821 LPLONG lpBufferSize)
823 LONG nRequired;
824 int path_len = strlenW(pContainer->path);
825 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
826 if (Directory >= pHeader->DirectoryCount)
828 *lpBufferSize = 0;
829 return FALSE;
832 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
833 if (nRequired < *lpBufferSize)
835 int dir_len;
837 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
838 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
839 wszPath[dir_len + path_len] = '\\';
840 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
841 *lpBufferSize = nRequired;
842 return TRUE;
844 *lpBufferSize = nRequired;
845 return FALSE;
848 /***********************************************************************
849 * URLCache_LocalFileNameToPathA (Internal)
851 * Copies the full path to the specified buffer given the local file
852 * name and the index of the directory it is in. Always sets value in
853 * lpBufferSize to the required buffer size.
855 * RETURNS
856 * TRUE if the buffer was big enough
857 * FALSE if the buffer was too small
860 static BOOL URLCache_LocalFileNameToPathA(
861 const URLCACHECONTAINER * pContainer,
862 LPCURLCACHE_HEADER pHeader,
863 LPCSTR szLocalFileName,
864 BYTE Directory,
865 LPSTR szPath,
866 LPLONG lpBufferSize)
868 LONG nRequired;
869 int path_len, file_name_len, dir_len;
871 if (Directory >= pHeader->DirectoryCount)
873 *lpBufferSize = 0;
874 return FALSE;
877 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
878 file_name_len = strlen(szLocalFileName);
879 dir_len = DIR_LENGTH;
881 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(WCHAR);
882 if (nRequired < *lpBufferSize)
884 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
885 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
886 szPath[path_len + dir_len] = '\\';
887 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
888 *lpBufferSize = nRequired;
889 return TRUE;
891 *lpBufferSize = nRequired;
892 return FALSE;
895 /***********************************************************************
896 * URLCache_CopyEntry (Internal)
898 * Copies an entry from the cache index file to the Win32 structure
900 * RETURNS
901 * TRUE if the buffer was big enough
902 * FALSE if the buffer was too small
905 static BOOL URLCache_CopyEntry(
906 URLCACHECONTAINER * pContainer,
907 LPCURLCACHE_HEADER pHeader,
908 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
909 LPDWORD lpdwBufferSize,
910 URL_CACHEFILE_ENTRY * pUrlEntry,
911 BOOL bUnicode)
913 int lenUrl;
914 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
916 if (*lpdwBufferSize >= dwRequiredSize)
918 lpCacheEntryInfo->lpHeaderInfo = NULL;
919 lpCacheEntryInfo->lpszFileExtension = NULL;
920 lpCacheEntryInfo->lpszLocalFileName = NULL;
921 lpCacheEntryInfo->lpszSourceUrlName = NULL;
922 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
923 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
924 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
925 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
926 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
927 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
928 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
929 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
930 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
931 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
932 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
933 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
934 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
935 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
938 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
939 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
940 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
941 if (bUnicode)
942 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
943 else
944 lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
945 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
947 /* FIXME: is source url optional? */
948 if (*lpdwBufferSize >= dwRequiredSize)
950 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
951 if (bUnicode)
952 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
953 else
954 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
957 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
958 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
959 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
961 if (pUrlEntry->dwOffsetLocalName)
963 LONG nLocalFilePathSize;
964 LPSTR lpszLocalFileName;
965 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
966 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
967 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
968 URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
970 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
972 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
974 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
975 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
976 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
978 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
980 if (*lpdwBufferSize >= dwRequiredSize)
982 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
983 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
984 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
986 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
987 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
988 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
990 if (dwRequiredSize > *lpdwBufferSize)
992 *lpdwBufferSize = dwRequiredSize;
993 SetLastError(ERROR_INSUFFICIENT_BUFFER);
994 return FALSE;
996 *lpdwBufferSize = dwRequiredSize;
997 return TRUE;
1001 /***********************************************************************
1002 * URLCache_SetEntryInfo (Internal)
1004 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1005 * according the the flags set by dwFieldControl.
1007 * RETURNS
1008 * TRUE if the buffer was big enough
1009 * FALSE if the buffer was too small
1012 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1014 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1015 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1016 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1017 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1018 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1019 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1020 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1021 FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1022 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1023 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1024 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1025 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1026 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1027 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1028 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1029 FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1031 return TRUE;
1034 /***********************************************************************
1035 * URLCache_HashKey (Internal)
1037 * Returns the hash key for a given string
1039 * RETURNS
1040 * hash key for the string
1043 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1045 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1046 * but the algorithm and result are not the same!
1048 static const unsigned char lookupTable[256] =
1050 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1051 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1052 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1053 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1054 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1055 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1056 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1057 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1058 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1059 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1060 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1061 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1062 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1063 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1064 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1065 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1066 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1067 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1068 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1069 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1070 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1071 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1072 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1073 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1074 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1075 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1076 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1077 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1078 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1079 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1080 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1081 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1083 BYTE key[4];
1084 DWORD i;
1085 int subscript[sizeof(key) / sizeof(key[0])];
1087 subscript[0] = *lpszKey;
1088 subscript[1] = (char)(*lpszKey + 1);
1089 subscript[2] = (char)(*lpszKey + 2);
1090 subscript[3] = (char)(*lpszKey + 3);
1092 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1093 key[i] = lookupTable[i];
1095 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1097 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1098 key[i] = lookupTable[*lpszKey ^ key[i]];
1101 return *(DWORD *)key;
1104 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1106 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1109 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1111 /* structure of hash table:
1112 * 448 entries divided into 64 blocks
1113 * each block therefore contains a chain of 7 key/offset pairs
1114 * how position in table is calculated:
1115 * 1. the url is hashed in helper function
1116 * 2. the key % 64 * 8 is the offset
1117 * 3. the key in the hash table is the hash key aligned to 64
1119 * note:
1120 * there can be multiple hash tables in the file and the offset to
1121 * the next one is stored in the header of the hash table
1123 DWORD key = URLCache_HashKey(lpszUrl);
1124 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1125 HASH_CACHEFILE_ENTRY * pHashEntry;
1126 DWORD dwHashTableNumber = 0;
1128 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1130 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1131 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1132 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1134 int i;
1135 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1137 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1138 continue;
1140 /* make sure that it is in fact a hash entry */
1141 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1143 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1144 continue;
1147 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1149 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1150 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1152 /* FIXME: we should make sure that this is the right element
1153 * before returning and claiming that it is. We can do this
1154 * by doing a simple compare between the URL we were given
1155 * and the URL stored in the entry. However, this assumes
1156 * we know the format of all the entries stored in the
1157 * hash table */
1158 *ppHashEntry = pHashElement;
1159 return TRUE;
1163 return FALSE;
1166 /***********************************************************************
1167 * URLCache_FindEntryInHash (Internal)
1169 * Searches all the hash tables in the index for the given URL and
1170 * returns the entry, if it was found, in ppEntry
1172 * RETURNS
1173 * TRUE if the entry was found
1174 * FALSE if the entry could not be found
1177 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
1179 struct _HASH_ENTRY * pHashEntry;
1180 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1182 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1183 return TRUE;
1185 return FALSE;
1188 /***********************************************************************
1189 * URLCache_HashEntrySetUse (Internal)
1191 * Searches all the hash tables in the index for the given URL and
1192 * sets the use count (stored or'ed with key)
1194 * RETURNS
1195 * TRUE if the entry was found
1196 * FALSE if the entry could not be found
1199 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
1201 struct _HASH_ENTRY * pHashEntry;
1202 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1204 pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1205 return TRUE;
1207 return FALSE;
1210 /***********************************************************************
1211 * URLCache_DeleteEntryFromHash (Internal)
1213 * Searches all the hash tables in the index for the given URL and
1214 * then if found deletes the entry.
1216 * RETURNS
1217 * TRUE if the entry was found
1218 * FALSE if the entry could not be found
1221 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1223 struct _HASH_ENTRY * pHashEntry;
1224 if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1226 pHashEntry->dwHashKey = HASHTABLE_FREE;
1227 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1228 return TRUE;
1230 return FALSE;
1233 /***********************************************************************
1234 * URLCache_AddEntryToHash (Internal)
1236 * Searches all the hash tables for a free slot based on the offset
1237 * generated from the hash key. If a free slot is found, the offset and
1238 * key are entered into the hash table.
1240 * RETURNS
1241 * TRUE if the entry was added
1242 * FALSE if the entry could not be added
1245 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1247 /* see URLCache_FindEntryInHash for structure of hash tables */
1249 DWORD key = URLCache_HashKey(lpszUrl);
1250 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1251 HASH_CACHEFILE_ENTRY * pHashEntry;
1252 DWORD dwHashTableNumber = 0;
1254 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1256 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1257 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1258 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1260 int i;
1261 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1263 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1264 break;
1266 /* make sure that it is in fact a hash entry */
1267 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1269 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1270 break;
1273 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1275 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1276 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1278 pHashElement->dwHashKey = key;
1279 pHashElement->dwOffsetEntry = dwOffsetEntry;
1280 return TRUE;
1284 pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1285 if (!pHashEntry)
1286 return FALSE;
1288 pHashEntry->HashTable[offset].dwHashKey = key;
1289 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1290 return TRUE;
1293 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1295 HASH_CACHEFILE_ENTRY *pHash;
1296 DWORD dwOffset;
1297 int i;
1299 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1301 FIXME("no free space for hash table\n");
1302 SetLastError(ERROR_DISK_FULL);
1303 return NULL;
1306 dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1308 if (pPrevHash)
1309 pPrevHash->dwAddressNext = dwOffset;
1310 else
1311 pHeader->dwOffsetFirstHashTable = dwOffset;
1312 pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1313 pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1314 pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1315 for (i = 0; i < HASHTABLE_SIZE; i++)
1317 pHash->HashTable[i].dwOffsetEntry = 0;
1318 pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1320 return pHash;
1323 /***********************************************************************
1324 * GetUrlCacheEntryInfoExA (WININET.@)
1327 BOOL WINAPI GetUrlCacheEntryInfoExA(
1328 LPCSTR lpszUrl,
1329 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1330 LPDWORD lpdwCacheEntryInfoBufSize,
1331 LPSTR lpszReserved,
1332 LPDWORD lpdwReserved,
1333 LPVOID lpReserved,
1334 DWORD dwFlags)
1336 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1337 debugstr_a(lpszUrl),
1338 lpCacheEntryInfo,
1339 lpdwCacheEntryInfoBufSize,
1340 lpszReserved,
1341 lpdwReserved,
1342 lpReserved,
1343 dwFlags);
1345 if ((lpszReserved != NULL) ||
1346 (lpdwReserved != NULL) ||
1347 (lpReserved != NULL))
1349 ERR("Reserved value was not 0\n");
1350 SetLastError(ERROR_INVALID_PARAMETER);
1351 return FALSE;
1353 if (dwFlags != 0)
1354 FIXME("Undocumented flag(s): %x\n", dwFlags);
1355 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1358 /***********************************************************************
1359 * GetUrlCacheEntryInfoA (WININET.@)
1362 BOOL WINAPI GetUrlCacheEntryInfoA(
1363 IN LPCSTR lpszUrlName,
1364 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1365 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1368 LPURLCACHE_HEADER pHeader;
1369 CACHEFILE_ENTRY * pEntry;
1370 URL_CACHEFILE_ENTRY * pUrlEntry;
1371 URLCACHECONTAINER * pContainer;
1373 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1375 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1376 return FALSE;
1378 if (!URLCacheContainer_OpenIndex(pContainer))
1379 return FALSE;
1381 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1382 return FALSE;
1384 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1386 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1387 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1388 SetLastError(ERROR_FILE_NOT_FOUND);
1389 return FALSE;
1392 if (pEntry->dwSignature != URL_SIGNATURE)
1394 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1395 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1396 SetLastError(ERROR_FILE_NOT_FOUND);
1397 return FALSE;
1400 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1401 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1402 if (pUrlEntry->dwOffsetHeaderInfo)
1403 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1405 if (!URLCache_CopyEntry(
1406 pContainer,
1407 pHeader,
1408 lpCacheEntryInfo,
1409 lpdwCacheEntryInfoBufferSize,
1410 pUrlEntry,
1411 FALSE /* ANSI */))
1413 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1414 return FALSE;
1416 TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1418 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1420 return TRUE;
1423 /***********************************************************************
1424 * GetUrlCacheEntryInfoW (WININET.@)
1427 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1428 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1429 LPDWORD lpdwCacheEntryInfoBufferSize)
1431 LPURLCACHE_HEADER pHeader;
1432 CACHEFILE_ENTRY * pEntry;
1433 URL_CACHEFILE_ENTRY * pUrlEntry;
1434 URLCACHECONTAINER * pContainer;
1435 LPSTR lpszUrlA;
1436 int url_len;
1438 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1440 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1441 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1442 if (!lpszUrlA)
1444 SetLastError(ERROR_OUTOFMEMORY);
1445 return FALSE;
1447 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1449 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1451 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1452 return FALSE;
1455 if (!URLCacheContainer_OpenIndex(pContainer))
1457 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1458 return FALSE;
1461 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1463 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1464 return FALSE;
1467 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1469 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1470 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1471 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1472 SetLastError(ERROR_FILE_NOT_FOUND);
1473 return FALSE;
1475 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1477 if (pEntry->dwSignature != URL_SIGNATURE)
1479 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1480 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1481 SetLastError(ERROR_FILE_NOT_FOUND);
1482 return FALSE;
1485 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1486 TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1487 TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1489 if (!URLCache_CopyEntry(
1490 pContainer,
1491 pHeader,
1492 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1493 lpdwCacheEntryInfoBufferSize,
1494 pUrlEntry,
1495 TRUE /* UNICODE */))
1497 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1498 return FALSE;
1500 TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1502 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1504 return TRUE;
1507 /***********************************************************************
1508 * GetUrlCacheEntryInfoExW (WININET.@)
1511 BOOL WINAPI GetUrlCacheEntryInfoExW(
1512 LPCWSTR lpszUrl,
1513 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1514 LPDWORD lpdwCacheEntryInfoBufSize,
1515 LPWSTR lpszReserved,
1516 LPDWORD lpdwReserved,
1517 LPVOID lpReserved,
1518 DWORD dwFlags)
1520 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1521 debugstr_w(lpszUrl),
1522 lpCacheEntryInfo,
1523 lpdwCacheEntryInfoBufSize,
1524 lpszReserved,
1525 lpdwReserved,
1526 lpReserved,
1527 dwFlags);
1529 if ((lpszReserved != NULL) ||
1530 (lpdwReserved != NULL) ||
1531 (lpReserved != NULL))
1533 ERR("Reserved value was not 0\n");
1534 SetLastError(ERROR_INVALID_PARAMETER);
1535 return FALSE;
1537 if (dwFlags != 0)
1538 FIXME("Undocumented flag(s): %x\n", dwFlags);
1539 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1542 /***********************************************************************
1543 * SetUrlCacheEntryInfoA (WININET.@)
1545 BOOL WINAPI SetUrlCacheEntryInfoA(
1546 LPCSTR lpszUrlName,
1547 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1548 DWORD dwFieldControl)
1550 LPURLCACHE_HEADER pHeader;
1551 CACHEFILE_ENTRY * pEntry;
1552 URLCACHECONTAINER * pContainer;
1554 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1556 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1557 return FALSE;
1559 if (!URLCacheContainer_OpenIndex(pContainer))
1560 return FALSE;
1562 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1563 return FALSE;
1565 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1567 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1568 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1569 SetLastError(ERROR_FILE_NOT_FOUND);
1570 return FALSE;
1573 if (pEntry->dwSignature != URL_SIGNATURE)
1575 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1576 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1577 SetLastError(ERROR_FILE_NOT_FOUND);
1578 return FALSE;
1581 URLCache_SetEntryInfo(
1582 (URL_CACHEFILE_ENTRY *)pEntry,
1583 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1584 dwFieldControl);
1586 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1588 return TRUE;
1591 /***********************************************************************
1592 * SetUrlCacheEntryInfoW (WININET.@)
1594 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1596 LPURLCACHE_HEADER pHeader;
1597 CACHEFILE_ENTRY * pEntry;
1598 URLCACHECONTAINER * pContainer;
1599 LPSTR lpszUrlA;
1600 int url_len;
1602 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1604 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1605 lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1606 if (!lpszUrlA)
1608 SetLastError(ERROR_OUTOFMEMORY);
1609 return FALSE;
1611 WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1613 if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1615 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1616 return FALSE;
1619 if (!URLCacheContainer_OpenIndex(pContainer))
1621 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1622 return FALSE;
1625 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1627 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1628 return FALSE;
1631 if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1633 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1634 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1635 WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1636 SetLastError(ERROR_FILE_NOT_FOUND);
1637 return FALSE;
1639 HeapFree(GetProcessHeap(), 0, lpszUrlA);
1641 if (pEntry->dwSignature != URL_SIGNATURE)
1643 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1644 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1645 SetLastError(ERROR_FILE_NOT_FOUND);
1646 return FALSE;
1649 URLCache_SetEntryInfo(
1650 (URL_CACHEFILE_ENTRY *)pEntry,
1651 lpCacheEntryInfo,
1652 dwFieldControl);
1654 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1656 return TRUE;
1659 /***********************************************************************
1660 * RetrieveUrlCacheEntryFileA (WININET.@)
1663 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1664 IN LPCSTR lpszUrlName,
1665 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1666 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1667 IN DWORD dwReserved
1670 LPURLCACHE_HEADER pHeader;
1671 CACHEFILE_ENTRY * pEntry;
1672 URL_CACHEFILE_ENTRY * pUrlEntry;
1673 URLCACHECONTAINER * pContainer;
1675 TRACE("(%s, %p, %p, 0x%08x)\n",
1676 debugstr_a(lpszUrlName),
1677 lpCacheEntryInfo,
1678 lpdwCacheEntryInfoBufferSize,
1679 dwReserved);
1681 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1682 return FALSE;
1684 if (!URLCacheContainer_OpenIndex(pContainer))
1685 return FALSE;
1687 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1688 return FALSE;
1690 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1692 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1693 TRACE("entry %s not found!\n", lpszUrlName);
1694 SetLastError(ERROR_FILE_NOT_FOUND);
1695 return FALSE;
1698 if (pEntry->dwSignature != URL_SIGNATURE)
1700 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1701 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1702 SetLastError(ERROR_FILE_NOT_FOUND);
1703 return FALSE;
1706 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1707 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1708 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1710 pUrlEntry->dwHitRate++;
1711 pUrlEntry->dwUseCount++;
1712 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1714 if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1716 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1717 return FALSE;
1719 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1721 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1723 return TRUE;
1726 /***********************************************************************
1727 * RetrieveUrlCacheEntryFileW (WININET.@)
1730 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1731 IN LPCWSTR lpszUrlName,
1732 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1733 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1734 IN DWORD dwReserved
1737 TRACE("(%s, %p, %p, 0x%08x)\n",
1738 debugstr_w(lpszUrlName),
1739 lpCacheEntryInfo,
1740 lpdwCacheEntryInfoBufferSize,
1741 dwReserved);
1743 return FALSE;
1746 /***********************************************************************
1747 * UnlockUrlCacheEntryFileA (WININET.@)
1750 BOOL WINAPI UnlockUrlCacheEntryFileA(
1751 IN LPCSTR lpszUrlName,
1752 IN DWORD dwReserved
1755 LPURLCACHE_HEADER pHeader;
1756 CACHEFILE_ENTRY * pEntry;
1757 URL_CACHEFILE_ENTRY * pUrlEntry;
1758 URLCACHECONTAINER * pContainer;
1760 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1762 if (dwReserved)
1764 ERR("dwReserved != 0\n");
1765 SetLastError(ERROR_INVALID_PARAMETER);
1766 return FALSE;
1769 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1770 return FALSE;
1772 if (!URLCacheContainer_OpenIndex(pContainer))
1773 return FALSE;
1775 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1776 return FALSE;
1778 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1780 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1781 TRACE("entry %s not found!\n", lpszUrlName);
1782 SetLastError(ERROR_FILE_NOT_FOUND);
1783 return FALSE;
1786 if (pEntry->dwSignature != URL_SIGNATURE)
1788 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1789 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1790 SetLastError(ERROR_FILE_NOT_FOUND);
1791 return FALSE;
1794 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1796 if (pUrlEntry->dwUseCount == 0)
1798 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1799 return FALSE;
1801 pUrlEntry->dwUseCount--;
1802 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1804 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1806 return TRUE;
1809 /***********************************************************************
1810 * UnlockUrlCacheEntryFileW (WININET.@)
1813 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1815 FIXME("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1816 return TRUE;
1819 /***********************************************************************
1820 * CreateUrlCacheEntryA (WININET.@)
1823 BOOL WINAPI CreateUrlCacheEntryA(
1824 IN LPCSTR lpszUrlName,
1825 IN DWORD dwExpectedFileSize,
1826 IN LPCSTR lpszFileExtension,
1827 OUT LPSTR lpszFileName,
1828 IN DWORD dwReserved
1831 DWORD len;
1832 WCHAR *url_name;
1833 WCHAR *file_extension;
1834 WCHAR file_name[MAX_PATH];
1835 BOOL bSuccess = FALSE;
1836 DWORD dwError = 0;
1838 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
1839 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1841 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
1842 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
1843 (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
1845 MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
1846 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
1848 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
1850 bSuccess = TRUE;
1852 else
1854 dwError = GetLastError();
1857 else
1859 dwError = GetLastError();
1861 HeapFree(GetProcessHeap(), 0, file_extension);
1863 else
1865 dwError = GetLastError();
1867 HeapFree(GetProcessHeap(), 0, url_name);
1868 if (!bSuccess)
1869 SetLastError(dwError);
1871 return bSuccess;
1873 /***********************************************************************
1874 * CreateUrlCacheEntryW (WININET.@)
1877 BOOL WINAPI CreateUrlCacheEntryW(
1878 IN LPCWSTR lpszUrlName,
1879 IN DWORD dwExpectedFileSize,
1880 IN LPCWSTR lpszFileExtension,
1881 OUT LPWSTR lpszFileName,
1882 IN DWORD dwReserved
1885 URLCACHECONTAINER * pContainer;
1886 LPURLCACHE_HEADER pHeader;
1887 CHAR szFile[MAX_PATH];
1888 WCHAR szExtension[MAX_PATH];
1889 LPCWSTR lpszUrlPart;
1890 LPCWSTR lpszUrlEnd;
1891 LPCWSTR lpszFileNameExtension;
1892 LPWSTR lpszFileNameNoPath;
1893 int i;
1894 int countnoextension;
1895 BYTE CacheDir;
1896 LONG lBufferSize;
1897 BOOL bFound = FALSE;
1898 int count;
1899 static const WCHAR szWWW[] = {'w','w','w',0};
1901 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
1902 debugstr_w(lpszUrlName),
1903 dwExpectedFileSize,
1904 debugstr_w(lpszFileExtension),
1905 lpszFileName,
1906 dwReserved);
1908 if (dwReserved)
1910 ERR("dwReserved != 0\n");
1911 SetLastError(ERROR_INVALID_PARAMETER);
1912 return FALSE;
1915 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1918 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
1919 lpszUrlEnd--;
1921 for (lpszUrlPart = lpszUrlEnd;
1922 (lpszUrlPart >= lpszUrlName);
1923 lpszUrlPart--)
1925 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
1927 bFound = TRUE;
1928 lpszUrlPart++;
1929 break;
1932 if (!lstrcmpW(lpszUrlPart, szWWW))
1934 lpszUrlPart += lstrlenW(szWWW);
1937 count = lpszUrlEnd - lpszUrlPart;
1939 if (bFound && (count < MAX_PATH))
1941 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
1942 if (!len)
1943 return FALSE;
1944 szFile[len] = '\0';
1945 /* FIXME: get rid of illegal characters like \, / and : */
1947 else
1949 FIXME("need to generate a random filename\n");
1952 TRACE("File name: %s\n", debugstr_a(szFile));
1954 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1955 return FALSE;
1957 if (!URLCacheContainer_OpenIndex(pContainer))
1958 return FALSE;
1960 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1961 return FALSE;
1963 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1965 lBufferSize = MAX_PATH * sizeof(WCHAR);
1966 URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1968 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1970 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
1971 lpszFileNameNoPath >= lpszFileName;
1972 --lpszFileNameNoPath)
1974 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
1975 break;
1978 countnoextension = lstrlenW(lpszFileNameNoPath);
1979 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
1980 if (lpszFileNameExtension)
1981 countnoextension -= lstrlenW(lpszFileNameExtension);
1982 *szExtension = '\0';
1984 if (lpszFileExtension)
1986 szExtension[0] = '.';
1987 lstrcpyW(szExtension+1, lpszFileExtension);
1990 for (i = 0; i < 255; i++)
1992 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
1993 HANDLE hFile;
1994 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
1995 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
1996 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1997 if (hFile != INVALID_HANDLE_VALUE)
1999 CloseHandle(hFile);
2000 return TRUE;
2004 return FALSE;
2008 /***********************************************************************
2009 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2011 * The bug we are compensating for is that some drongo at Microsoft
2012 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2013 * As a consequence, CommitUrlCacheEntryA has been effectively
2014 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2015 * is still defined as LPCWSTR. The result (other than madness) is
2016 * that we always need to store lpHeaderInfo in CP_ACP rather than
2017 * in UTF16, and we need to avoid converting lpHeaderInfo in
2018 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2019 * result will lose data for arbitrary binary data.
2022 static BOOL WINAPI CommitUrlCacheEntryInternal(
2023 IN LPCWSTR lpszUrlName,
2024 IN LPCWSTR lpszLocalFileName,
2025 IN FILETIME ExpireTime,
2026 IN FILETIME LastModifiedTime,
2027 IN DWORD CacheEntryType,
2028 IN LPBYTE lpHeaderInfo,
2029 IN DWORD dwHeaderSize,
2030 IN LPCWSTR lpszFileExtension,
2031 IN LPCWSTR lpszOriginalUrl
2034 URLCACHECONTAINER * pContainer;
2035 LPURLCACHE_HEADER pHeader;
2036 CACHEFILE_ENTRY * pEntry;
2037 URL_CACHEFILE_ENTRY * pUrlEntry;
2038 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2039 DWORD dwOffsetLocalFileName = 0;
2040 DWORD dwOffsetHeader = 0;
2041 DWORD dwFileSizeLow = 0;
2042 DWORD dwFileSizeHigh = 0;
2043 BYTE cDirectory = 0;
2044 char achFile[MAX_PATH];
2045 char achUrl[MAX_PATH];
2046 char *pchLocalFileName = 0;
2048 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2049 debugstr_w(lpszUrlName),
2050 debugstr_w(lpszLocalFileName),
2051 CacheEntryType,
2052 lpHeaderInfo,
2053 dwHeaderSize,
2054 debugstr_w(lpszFileExtension),
2055 debugstr_w(lpszOriginalUrl));
2057 if (lpszOriginalUrl)
2058 WARN(": lpszOriginalUrl ignored\n");
2060 if (lpszLocalFileName)
2062 HANDLE hFile;
2064 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2065 if (hFile == INVALID_HANDLE_VALUE)
2067 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2068 return FALSE;
2071 /* Get file size */
2072 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2073 if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2075 ERR("couldn't get file size (error is %d)\n", GetLastError());
2076 CloseHandle(hFile);
2077 return FALSE;
2080 CloseHandle(hFile);
2083 if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2084 return FALSE;
2086 if (!URLCacheContainer_OpenIndex(pContainer))
2087 return FALSE;
2089 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2090 return FALSE;
2092 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, achUrl, -1, NULL, NULL);
2094 if (URLCache_FindEntryInHash(pHeader, achUrl, &pEntry))
2096 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2097 FIXME("entry already in cache - don't know what to do!\n");
2099 * SetLastError(ERROR_FILE_NOT_FOUND);
2100 * return FALSE;
2102 return TRUE;
2105 if (lpszLocalFileName)
2107 BOOL bFound = FALSE;
2109 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2111 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2112 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2113 SetLastError(ERROR_INVALID_PARAMETER);
2114 return FALSE;
2117 /* skip container path prefix */
2118 lpszLocalFileName += lstrlenW(pContainer->path);
2120 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2121 pchLocalFileName = achFile;
2123 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2125 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2127 bFound = TRUE;
2128 break;
2132 if (!bFound)
2134 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2135 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2136 SetLastError(ERROR_INVALID_PARAMETER);
2137 return FALSE;
2140 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2143 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(achUrl) + 1);
2144 if (lpszLocalFileName)
2146 dwOffsetLocalFileName = dwBytesNeeded;
2147 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2149 if (lpHeaderInfo)
2151 dwOffsetHeader = dwBytesNeeded;
2152 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2155 /* round up to next block */
2156 if (dwBytesNeeded % BLOCKSIZE)
2158 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2159 dwBytesNeeded += BLOCKSIZE;
2162 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2164 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2165 ERR("no free entries\n");
2166 SetLastError(ERROR_DISK_FULL);
2167 return FALSE;
2170 /* FindFirstFreeEntry fills in blocks used */
2171 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2172 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2173 pUrlEntry->CacheDir = cDirectory;
2174 pUrlEntry->CacheEntryType = CacheEntryType;
2175 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2176 pUrlEntry->dwExemptDelta = 0;
2177 pUrlEntry->dwHitRate = 0;
2178 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2179 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2180 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2181 pUrlEntry->dwSizeHigh = 0;
2182 pUrlEntry->dwSizeLow = dwFileSizeLow;
2183 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2184 pUrlEntry->dwUseCount = 0;
2185 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2186 pUrlEntry->LastModifiedTime = LastModifiedTime;
2187 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2188 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2189 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2190 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2192 /*** Unknowns ***/
2193 pUrlEntry->dwUnknown1 = 0;
2194 pUrlEntry->dwUnknown2 = 0;
2195 pUrlEntry->dwUnknown3 = 0x60;
2196 pUrlEntry->Unknown4 = 0;
2197 pUrlEntry->wUnknown5 = 0x1010;
2198 pUrlEntry->dwUnknown6 = 0;
2199 pUrlEntry->dwUnknown7 = 0;
2200 pUrlEntry->dwUnknown8 = 0;
2203 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, achUrl);
2204 if (dwOffsetLocalFileName)
2205 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2206 if (dwOffsetHeader)
2207 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2209 if (!URLCache_AddEntryToHash(pHeader, achUrl, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2211 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2212 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2213 return FALSE;
2216 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2218 return TRUE;
2221 /***********************************************************************
2222 * CommitUrlCacheEntryA (WININET.@)
2225 BOOL WINAPI CommitUrlCacheEntryA(
2226 IN LPCSTR lpszUrlName,
2227 IN LPCSTR lpszLocalFileName,
2228 IN FILETIME ExpireTime,
2229 IN FILETIME LastModifiedTime,
2230 IN DWORD CacheEntryType,
2231 IN LPBYTE lpHeaderInfo,
2232 IN DWORD dwHeaderSize,
2233 IN LPCSTR lpszFileExtension,
2234 IN LPCSTR lpszOriginalUrl
2237 DWORD len;
2238 WCHAR *url_name;
2239 WCHAR *local_file_name;
2240 WCHAR *original_url = NULL;
2241 BOOL bSuccess = FALSE;
2242 DWORD dwError = 0;
2244 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2245 debugstr_a(lpszUrlName),
2246 debugstr_a(lpszLocalFileName),
2247 CacheEntryType,
2248 lpHeaderInfo,
2249 dwHeaderSize,
2250 debugstr_a(lpszFileExtension),
2251 debugstr_a(lpszOriginalUrl));
2253 if (lpszFileExtension != 0)
2255 SetLastError(ERROR_INVALID_PARAMETER);
2256 return FALSE;
2258 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2259 (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2261 MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2262 if ((len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0)) != 0 &&
2263 (local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2265 MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2266 if (!lpszOriginalUrl ||
2267 ((len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0)) != 0 &&
2268 (original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0))
2270 if (original_url)
2271 MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2272 if (CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2273 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2274 NULL, original_url))
2276 bSuccess = TRUE;
2278 else
2280 dwError = GetLastError();
2282 HeapFree(GetProcessHeap(), 0, original_url);
2284 else
2286 dwError = GetLastError();
2288 HeapFree(GetProcessHeap(), 0, local_file_name);
2290 else
2292 dwError = GetLastError();
2294 HeapFree(GetProcessHeap(), 0, url_name);
2295 if (!bSuccess)
2296 SetLastError(dwError);
2298 return bSuccess;
2301 /***********************************************************************
2302 * CommitUrlCacheEntryW (WININET.@)
2305 BOOL WINAPI CommitUrlCacheEntryW(
2306 IN LPCWSTR lpszUrlName,
2307 IN LPCWSTR lpszLocalFileName,
2308 IN FILETIME ExpireTime,
2309 IN FILETIME LastModifiedTime,
2310 IN DWORD CacheEntryType,
2311 IN LPWSTR lpHeaderInfo,
2312 IN DWORD dwHeaderSize,
2313 IN LPCWSTR lpszFileExtension,
2314 IN LPCWSTR lpszOriginalUrl
2317 DWORD dwError = 0;
2318 BOOL bSuccess = FALSE;
2319 DWORD len = 0;
2320 CHAR *header_info = NULL;
2322 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2323 debugstr_w(lpszUrlName),
2324 debugstr_w(lpszLocalFileName),
2325 CacheEntryType,
2326 lpHeaderInfo,
2327 dwHeaderSize,
2328 debugstr_w(lpszFileExtension),
2329 debugstr_w(lpszOriginalUrl));
2331 if (!lpHeaderInfo ||
2332 ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2333 (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2335 if (header_info)
2336 WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2337 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2338 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2340 bSuccess = TRUE;
2342 else
2344 dwError = GetLastError();
2346 if (header_info)
2348 HeapFree(GetProcessHeap(), 0, header_info);
2349 if (!bSuccess)
2350 SetLastError(dwError);
2353 return bSuccess;
2356 /***********************************************************************
2357 * ReadUrlCacheEntryStream (WININET.@)
2360 BOOL WINAPI ReadUrlCacheEntryStream(
2361 IN HANDLE hUrlCacheStream,
2362 IN DWORD dwLocation,
2363 IN OUT LPVOID lpBuffer,
2364 IN OUT LPDWORD lpdwLen,
2365 IN DWORD dwReserved
2368 /* Get handle to file from 'stream' */
2369 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2371 if (dwReserved != 0)
2373 ERR("dwReserved != 0\n");
2374 SetLastError(ERROR_INVALID_PARAMETER);
2375 return FALSE;
2378 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2380 SetLastError(ERROR_INVALID_HANDLE);
2381 return FALSE;
2384 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2385 return FALSE;
2386 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2389 /***********************************************************************
2390 * RetrieveUrlCacheEntryStreamA (WININET.@)
2393 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2394 IN LPCSTR lpszUrlName,
2395 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2396 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2397 IN BOOL fRandomRead,
2398 IN DWORD dwReserved
2401 /* NOTE: this is not the same as the way that the native
2402 * version allocates 'stream' handles. I did it this way
2403 * as it is much easier and no applications should depend
2404 * on this behaviour. (Native version appears to allocate
2405 * indices into a table)
2407 STREAM_HANDLE * pStream;
2408 HANDLE hFile;
2410 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2411 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2413 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2414 lpCacheEntryInfo,
2415 lpdwCacheEntryInfoBufferSize,
2416 dwReserved))
2418 return NULL;
2421 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2422 GENERIC_READ,
2423 FILE_SHARE_READ,
2424 NULL,
2425 OPEN_EXISTING,
2426 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2427 NULL);
2428 if (hFile == INVALID_HANDLE_VALUE)
2429 return FALSE;
2431 /* allocate handle storage space */
2432 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2433 if (!pStream)
2435 CloseHandle(hFile);
2436 SetLastError(ERROR_OUTOFMEMORY);
2437 return FALSE;
2440 pStream->hFile = hFile;
2441 strcpy(pStream->lpszUrl, lpszUrlName);
2442 return (HANDLE)pStream;
2445 /***********************************************************************
2446 * RetrieveUrlCacheEntryStreamW (WININET.@)
2449 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2450 IN LPCWSTR lpszUrlName,
2451 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2452 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2453 IN BOOL fRandomRead,
2454 IN DWORD dwReserved
2457 FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2458 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2459 return NULL;
2462 /***********************************************************************
2463 * UnlockUrlCacheEntryStream (WININET.@)
2466 BOOL WINAPI UnlockUrlCacheEntryStream(
2467 IN HANDLE hUrlCacheStream,
2468 IN DWORD dwReserved
2471 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2473 if (dwReserved != 0)
2475 ERR("dwReserved != 0\n");
2476 SetLastError(ERROR_INVALID_PARAMETER);
2477 return FALSE;
2480 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2482 SetLastError(ERROR_INVALID_HANDLE);
2483 return FALSE;
2486 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2487 return FALSE;
2489 /* close file handle */
2490 CloseHandle(pStream->hFile);
2492 /* free allocated space */
2493 HeapFree(GetProcessHeap(), 0, pStream);
2495 return TRUE;
2499 /***********************************************************************
2500 * DeleteUrlCacheEntryA (WININET.@)
2503 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2505 URLCACHECONTAINER * pContainer;
2506 LPURLCACHE_HEADER pHeader;
2507 CACHEFILE_ENTRY * pEntry;
2509 TRACE("(%s)\n", debugstr_a(lpszUrlName));
2511 if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2512 return FALSE;
2514 if (!URLCacheContainer_OpenIndex(pContainer))
2515 return FALSE;
2517 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2518 return FALSE;
2520 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2522 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2523 TRACE("entry %s not found!\n", lpszUrlName);
2524 SetLastError(ERROR_FILE_NOT_FOUND);
2525 return FALSE;
2528 URLCache_DeleteEntry(pHeader, pEntry);
2530 URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2532 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2534 return TRUE;
2537 /***********************************************************************
2538 * DeleteUrlCacheEntryW (WININET.@)
2541 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2543 FIXME("(%s) stub\n", debugstr_w(lpszUrlName));
2544 return TRUE;
2547 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2549 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2550 return TRUE;
2553 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2555 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2556 return TRUE;
2559 /***********************************************************************
2560 * CreateCacheContainerA (WININET.@)
2562 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2563 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2565 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2566 d1, d2, d3, d4, d5, d6, d7, d8);
2567 return TRUE;
2570 /***********************************************************************
2571 * CreateCacheContainerW (WININET.@)
2573 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2574 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2576 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2577 d1, d2, d3, d4, d5, d6, d7, d8);
2578 return TRUE;
2581 /***********************************************************************
2582 * FindCloseUrlCache (WININET.@)
2584 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
2586 FIXME("(%p) stub\n", hEnumHandle);
2587 return TRUE;
2590 /***********************************************************************
2591 * FindFirstUrlCacheContainerA (WININET.@)
2593 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2595 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2596 return NULL;
2599 /***********************************************************************
2600 * FindFirstUrlCacheContainerW (WININET.@)
2602 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2604 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2605 return NULL;
2608 /***********************************************************************
2609 * FindNextUrlCacheContainerA (WININET.@)
2611 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2613 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2614 return FALSE;
2617 /***********************************************************************
2618 * FindNextUrlCacheContainerW (WININET.@)
2620 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2622 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2623 return FALSE;
2626 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2627 LPCSTR lpszUrlSearchPattern,
2628 DWORD dwFlags,
2629 DWORD dwFilter,
2630 GROUPID GroupId,
2631 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2632 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2633 LPVOID lpReserved,
2634 LPDWORD pcbReserved2,
2635 LPVOID lpReserved3
2638 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2639 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2640 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2641 SetLastError(ERROR_FILE_NOT_FOUND);
2642 return NULL;
2645 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2646 LPCWSTR lpszUrlSearchPattern,
2647 DWORD dwFlags,
2648 DWORD dwFilter,
2649 GROUPID GroupId,
2650 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2651 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2652 LPVOID lpReserved,
2653 LPDWORD pcbReserved2,
2654 LPVOID lpReserved3
2657 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2658 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2659 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2660 SetLastError(ERROR_FILE_NOT_FOUND);
2661 return NULL;
2664 /***********************************************************************
2665 * FindFirstUrlCacheEntryA (WININET.@)
2668 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2669 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2671 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2672 SetLastError(ERROR_FILE_NOT_FOUND);
2673 return 0;
2676 /***********************************************************************
2677 * FindFirstUrlCacheEntryW (WININET.@)
2680 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2681 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2683 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2684 return 0;
2687 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
2688 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
2690 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
2691 dwSearchCondition, lpGroupId, lpReserved);
2692 return NULL;
2695 BOOL WINAPI FindNextUrlCacheEntryA(
2696 HANDLE hEnumHandle,
2697 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
2698 LPDWORD lpdwNextCacheEntryInfoBufferSize
2701 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2702 return FALSE;
2705 BOOL WINAPI FindNextUrlCacheEntryW(
2706 HANDLE hEnumHandle,
2707 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
2708 LPDWORD lpdwNextCacheEntryInfoBufferSize
2711 FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
2712 return FALSE;
2715 BOOL WINAPI FindNextUrlCacheEntryExA(
2716 HANDLE hEnumHandle,
2717 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2718 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2719 LPVOID lpReserved,
2720 LPDWORD pcbReserved2,
2721 LPVOID lpReserved3
2724 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2725 lpReserved, pcbReserved2, lpReserved3);
2726 return FALSE;
2729 BOOL WINAPI FindNextUrlCacheEntryExW(
2730 HANDLE hEnumHandle,
2731 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2732 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2733 LPVOID lpReserved,
2734 LPDWORD pcbReserved2,
2735 LPVOID lpReserved3
2738 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
2739 lpReserved, pcbReserved2, lpReserved3);
2740 return FALSE;
2743 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
2745 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
2746 return FALSE;
2749 /***********************************************************************
2750 * CreateUrlCacheGroup (WININET.@)
2753 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
2755 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
2756 return FALSE;
2759 /***********************************************************************
2760 * DeleteUrlCacheGroup (WININET.@)
2763 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2765 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
2766 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
2767 return FALSE;
2770 /***********************************************************************
2771 * SetUrlCacheEntryGroupA (WININET.@)
2774 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
2775 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2776 LPVOID lpReserved)
2778 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2779 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2780 pbGroupAttributes, cbGroupAttributes, lpReserved);
2781 SetLastError(ERROR_FILE_NOT_FOUND);
2782 return FALSE;
2785 /***********************************************************************
2786 * SetUrlCacheEntryGroupW (WININET.@)
2789 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
2790 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2791 LPVOID lpReserved)
2793 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
2794 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
2795 pbGroupAttributes, cbGroupAttributes, lpReserved);
2796 SetLastError(ERROR_FILE_NOT_FOUND);
2797 return FALSE;
2800 /***********************************************************************
2801 * GetUrlCacheConfigInfoW (WININET.@)
2803 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2805 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2806 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2807 return FALSE;
2810 /***********************************************************************
2811 * GetUrlCacheConfigInfoA (WININET.@)
2813 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2815 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2817 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
2818 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2819 return FALSE;
2822 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2823 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
2824 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2826 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2827 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2828 lpdwGroupInfo, lpReserved);
2829 return FALSE;
2832 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2833 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
2834 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
2836 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
2837 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
2838 lpdwGroupInfo, lpReserved);
2839 return FALSE;
2842 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2843 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
2845 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2846 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2847 return TRUE;
2850 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
2851 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
2853 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
2854 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
2855 return TRUE;
2858 BOOL WINAPI SetUrlCacheConfigInfoA( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2860 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2861 return TRUE;
2864 BOOL WINAPI SetUrlCacheConfigInfoW( LPDWORD lpCacheConfigInfo, DWORD dwFieldControl )
2866 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
2867 return TRUE;
2870 /***********************************************************************
2871 * DeleteIE3Cache (WININET.@)
2873 * Deletes the files used by the IE3 URL caching system.
2875 * PARAMS
2876 * hWnd [I] A dummy window.
2877 * hInst [I] Instance of process calling the function.
2878 * lpszCmdLine [I] Options used by function.
2879 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
2881 * RETURNS
2882 * nothing
2884 void WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
2886 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);