* Slap *some* sense into our header inclusions.
[reactos.git] / reactos / dll / win32 / wininet / urlcache.c
1 /*
2 * Wininet - Url Cache functions
3 *
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
6 *
7 * Eric Kohl
8 * Aric Stewart
9 *
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.
14 *
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.
19 *
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
23 */
24
25 #define WIN32_NO_STATUS
26 #define _INC_WINDOWS
27 #define COM_NO_WINDOWS_H
28
29 #include <config.h>
30 //#include "wine/port.h"
31
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34
35 #include <stdarg.h>
36 //#include <stdio.h>
37 //#include <stdlib.h>
38 //#include <string.h>
39 //#include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 //#include <time.h>
44
45 #include <windef.h>
46 #include <winbase.h>
47 //#include "winuser.h"
48 #include <wininet.h>
49 #include <winineti.h>
50 //#include "winerror.h"
51 #include <winreg.h>
52 #include <shlwapi.h>
53 #include <shlobj.h>
54 #include <shellapi.h>
55
56 #if defined(__MINGW32__) || defined (_MSC_VER)
57 #include <ws2tcpip.h>
58 #endif
59
60 #include "internet.h"
61
62 //#include "wine/unicode.h"
63 #include <wine/debug.h>
64
65 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
66
67 #define ENTRY_START_OFFSET 0x4000
68 #define DIR_LENGTH 8
69 #define BLOCKSIZE 128
70 #define HASHTABLE_SIZE 448
71 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
72 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
73 #define ALLOCATION_TABLE_OFFSET 0x250
74 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
75 #define MIN_BLOCK_NO 0x80
76 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
77 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
78
79 #define HASHTABLE_URL 0
80 #define HASHTABLE_DEL 1
81 #define HASHTABLE_LOCK 2
82 #define HASHTABLE_FREE 3
83 #define HASHTABLE_REDR 5
84 #define HASHTABLE_FLAG_BITS 5
85
86 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
87 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
88 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
89 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
90 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
91
92 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
93
94 typedef struct _CACHEFILE_ENTRY
95 {
96 /* union
97 {*/
98 DWORD dwSignature; /* e.g. "URL " */
99 /* CHAR szSignature[4];
100 };*/
101 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
102 } CACHEFILE_ENTRY;
103
104 typedef struct _URL_CACHEFILE_ENTRY
105 {
106 CACHEFILE_ENTRY CacheFileEntry;
107 FILETIME LastModifiedTime;
108 FILETIME LastAccessTime;
109 WORD wExpiredDate; /* expire date in dos format */
110 WORD wExpiredTime; /* expire time in dos format */
111 DWORD dwUnknown1; /* usually zero */
112 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
113 DWORD dwUnknown2; /* usually zero */
114 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
115 DWORD dwUnknown3; /* usually 0x60 */
116 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
117 BYTE CacheDir; /* index of cache directory this url is stored in */
118 BYTE Unknown4; /* usually zero */
119 WORD wUnknown5; /* usually 0x1010 */
120 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
121 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
122 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
123 DWORD dwHeaderInfoSize;
124 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
125 WORD wLastSyncDate; /* last sync date in dos format */
126 WORD wLastSyncTime; /* last sync time in dos format */
127 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
128 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
129 WORD wUnknownDate; /* usually same as wLastSyncDate */
130 WORD wUnknownTime; /* usually same as wLastSyncTime */
131 DWORD dwUnknown7; /* usually zero */
132 DWORD dwUnknown8; /* usually zero */
133 /* packing to dword align start of next field */
134 /* CHAR szSourceUrlName[]; (url) */
135 /* packing to dword align start of next field */
136 /* CHAR szLocalFileName[]; (local file name excluding path) */
137 /* packing to dword align start of next field */
138 /* CHAR szHeaderInfo[]; (header info) */
139 } URL_CACHEFILE_ENTRY;
140
141 struct _HASH_ENTRY
142 {
143 DWORD dwHashKey;
144 DWORD dwOffsetEntry;
145 };
146
147 typedef struct _HASH_CACHEFILE_ENTRY
148 {
149 CACHEFILE_ENTRY CacheFileEntry;
150 DWORD dwAddressNext;
151 DWORD dwHashTableNumber;
152 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
153 } HASH_CACHEFILE_ENTRY;
154
155 typedef struct _DIRECTORY_DATA
156 {
157 DWORD dwNumFiles;
158 char filename[DIR_LENGTH];
159 } DIRECTORY_DATA;
160
161 typedef struct _URLCACHE_HEADER
162 {
163 char szSignature[28];
164 DWORD dwFileSize;
165 DWORD dwOffsetFirstHashTable;
166 DWORD dwIndexCapacityInBlocks;
167 DWORD dwBlocksInUse;
168 DWORD dwUnknown1;
169 ULARGE_INTEGER CacheLimit;
170 ULARGE_INTEGER CacheUsage;
171 ULARGE_INTEGER ExemptUsage;
172 DWORD DirectoryCount; /* number of directory_data's */
173 DIRECTORY_DATA directory_data[1]; /* first directory entry */
174 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
175 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
176
177 typedef struct _STREAM_HANDLE
178 {
179 HANDLE hFile;
180 CHAR lpszUrl[1];
181 } STREAM_HANDLE;
182
183 typedef struct _URLCACHECONTAINER
184 {
185 struct list entry; /* part of a list */
186 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
187 LPWSTR path; /* path to url container directory */
188 HANDLE hMapping; /* handle of file mapping */
189 DWORD file_size; /* size of file when mapping was opened */
190 HANDLE hMutex; /* handle of mutex */
191 } URLCACHECONTAINER;
192
193
194 /* List of all containers available */
195 static struct list UrlContainers = LIST_INIT(UrlContainers);
196 // ReactOS r54992
197 BOOL bDefaultContainersAdded = FALSE;
198
199 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
200
201 /***********************************************************************
202 * URLCache_PathToObjectName (Internal)
203 *
204 * Converts a path to a name suitable for use as a Win32 object name.
205 * Replaces '\\' characters in-place with the specified character
206 * (usually '_' or '!')
207 *
208 * RETURNS
209 * nothing
210 *
211 */
212 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
213 {
214 for (; *lpszPath; lpszPath++)
215 {
216 if (*lpszPath == '\\')
217 *lpszPath = replace;
218 }
219 }
220
221 /***********************************************************************
222 * URLCacheContainer_OpenIndex (Internal)
223 *
224 * Opens the index file and saves mapping handle in hCacheIndexMapping
225 *
226 * RETURNS
227 * ERROR_SUCCESS if succeeded
228 * Any other Win32 error code if failed
229 *
230 */
231 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer, DWORD blocks_no)
232 {
233 HANDLE hFile;
234 WCHAR wszFilePath[MAX_PATH];
235 DWORD dwFileSize, new_file_size;
236
237 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
238 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
239
240 WaitForSingleObject(pContainer->hMutex, INFINITE);
241
242 if (pContainer->hMapping) {
243 ReleaseMutex(pContainer->hMutex);
244 return ERROR_SUCCESS;
245 }
246
247 strcpyW(wszFilePath, pContainer->path);
248 strcatW(wszFilePath, wszIndex);
249
250 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
251 if (hFile == INVALID_HANDLE_VALUE)
252 {
253 /* Maybe the directory wasn't there? Try to create it */
254 if (CreateDirectoryW(pContainer->path, 0))
255 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
256 }
257 if (hFile == INVALID_HANDLE_VALUE)
258 {
259 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
260 ReleaseMutex(pContainer->hMutex);
261 return GetLastError();
262 }
263
264 dwFileSize = GetFileSize(hFile, NULL);
265 if (dwFileSize == INVALID_FILE_SIZE)
266 {
267 ReleaseMutex(pContainer->hMutex);
268 return GetLastError();
269 }
270
271 if (blocks_no < MIN_BLOCK_NO)
272 blocks_no = MIN_BLOCK_NO;
273 else if (blocks_no > MAX_BLOCK_NO)
274 blocks_no = MAX_BLOCK_NO;
275 new_file_size = FILE_SIZE(blocks_no);
276
277 if (dwFileSize < new_file_size)
278 {
279 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
280 HKEY key;
281 char achZeroes[0x1000];
282 DWORD dwOffset;
283 DWORD dwError = ERROR_SUCCESS;
284
285 if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
286 dwError = GetLastError();
287
288 /* Write zeroes to the entire file so we can safely map it without
289 * fear of getting a SEGV because the disk is full.
290 */
291 memset(achZeroes, 0, sizeof(achZeroes));
292 for (dwOffset = dwFileSize; dwOffset<new_file_size && dwError==ERROR_SUCCESS;
293 dwOffset += sizeof(achZeroes))
294 {
295 DWORD dwWrite = sizeof(achZeroes);
296 DWORD dwWritten;
297
298 if (new_file_size - dwOffset < dwWrite)
299 dwWrite = new_file_size - dwOffset;
300 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
301 dwWritten != dwWrite)
302 {
303 /* If we fail to write, we need to return the error that
304 * cause the problem and also make sure the file is no
305 * longer there, if possible.
306 */
307 dwError = GetLastError();
308 }
309 }
310
311 if (dwError == ERROR_SUCCESS)
312 {
313 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
314
315 if (hMapping)
316 {
317 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
318
319 if (pHeader && dwFileSize)
320 {
321 pHeader->dwFileSize = new_file_size;
322 pHeader->dwIndexCapacityInBlocks = blocks_no;
323 }
324 else if (pHeader)
325 {
326 WCHAR *pwchDir;
327 WCHAR wszDirPath[MAX_PATH];
328 FILETIME ft;
329 int i, j;
330 HASH_CACHEFILE_ENTRY *pHashEntry;
331
332 /* First set some constants and defaults in the header */
333 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
334 pHeader->dwFileSize = new_file_size;
335 pHeader->dwIndexCapacityInBlocks = blocks_no;
336 /* 127MB - taken from default for Windows 2000 */
337 pHeader->CacheLimit.QuadPart = 0x07ff5400;
338 /* Copied from a Windows 2000 cache index */
339 pHeader->DirectoryCount = 4;
340
341 /* If the registry has a cache size set, use the registry value */
342 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
343 {
344 DWORD dw;
345 DWORD len = sizeof(dw);
346 DWORD keytype;
347
348 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
349 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
350 keytype == REG_DWORD)
351 {
352 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
353 }
354 RegCloseKey(key);
355 }
356
357 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
358
359 /* Last step - create the directories */
360
361 strcpyW(wszDirPath, pContainer->path);
362 pwchDir = wszDirPath + strlenW(wszDirPath);
363 pwchDir[8] = 0;
364
365 GetSystemTimeAsFileTime(&ft);
366
367 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
368 {
369 pHeader->directory_data[i].dwNumFiles = 0;
370 for (j = 0;; ++j)
371 {
372 int k;
373 ULONGLONG n = ft.dwHighDateTime;
374
375 /* Generate a file name to attempt to create.
376 * This algorithm will create what will appear
377 * to be random and unrelated directory names
378 * of up to 9 characters in length.
379 */
380 n <<= 32;
381 n += ft.dwLowDateTime;
382 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
383
384 for (k = 0; k < 8; ++k)
385 {
386 int r = (n % 36);
387
388 /* Dividing by a prime greater than 36 helps
389 * with the appearance of randomness
390 */
391 n /= 37;
392
393 if (r < 10)
394 pwchDir[k] = '0' + r;
395 else
396 pwchDir[k] = 'A' + (r - 10);
397 }
398
399 if (CreateDirectoryW(wszDirPath, 0))
400 {
401 /* The following is OK because we generated an
402 * 8 character directory name made from characters
403 * [A-Z0-9], which are equivalent for all code
404 * pages and for UTF-16
405 */
406 for (k = 0; k < 8; ++k)
407 pHeader->directory_data[i].filename[k] = pwchDir[k];
408 break;
409 }
410 else if (j >= 255)
411 {
412 /* Give up. The most likely cause of this
413 * is a full disk, but whatever the cause
414 * is, it should be more than apparent that
415 * we won't succeed.
416 */
417 dwError = GetLastError();
418 break;
419 }
420 }
421 }
422
423 UnmapViewOfFile(pHeader);
424 }
425 else
426 {
427 dwError = GetLastError();
428 }
429 dwFileSize = new_file_size;
430 CloseHandle(hMapping);
431 }
432 else
433 {
434 dwError = GetLastError();
435 }
436 }
437
438 if (dwError)
439 {
440 CloseHandle(hFile);
441 DeleteFileW(wszFilePath);
442 ReleaseMutex(pContainer->hMutex);
443 return dwError;
444 }
445
446 }
447
448 pContainer->file_size = dwFileSize;
449 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
450 URLCache_PathToObjectName(wszFilePath, '_');
451 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
452 if (!pContainer->hMapping)
453 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
454 CloseHandle(hFile);
455 if (!pContainer->hMapping)
456 {
457 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
458 ReleaseMutex(pContainer->hMutex);
459 return GetLastError();
460 }
461
462 ReleaseMutex(pContainer->hMutex);
463
464 return ERROR_SUCCESS;
465 }
466
467 /***********************************************************************
468 * URLCacheContainer_CloseIndex (Internal)
469 *
470 * Closes the index
471 *
472 * RETURNS
473 * nothing
474 *
475 */
476 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
477 {
478 CloseHandle(pContainer->hMapping);
479 pContainer->hMapping = NULL;
480 }
481
482 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
483 {
484 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
485 int cache_prefix_len = strlenW(cache_prefix);
486
487 if (!pContainer)
488 {
489 return FALSE;
490 }
491
492 pContainer->hMapping = NULL;
493 pContainer->file_size = 0;
494
495 pContainer->path = heap_strdupW(path);
496 if (!pContainer->path)
497 {
498 heap_free(pContainer);
499 return FALSE;
500 }
501
502 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
503 if (!pContainer->cache_prefix)
504 {
505 heap_free(pContainer->path);
506 heap_free(pContainer);
507 return FALSE;
508 }
509
510 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
511
512 CharLowerW(mutex_name);
513 URLCache_PathToObjectName(mutex_name, '!');
514
515 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
516 {
517 ERR("couldn't create mutex (error is %d)\n", GetLastError());
518 heap_free(pContainer->path);
519 heap_free(pContainer);
520 return FALSE;
521 }
522
523 list_add_head(&UrlContainers, &pContainer->entry);
524
525 return TRUE;
526 }
527
528 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
529 {
530 list_remove(&pContainer->entry);
531
532 URLCacheContainer_CloseIndex(pContainer);
533 CloseHandle(pContainer->hMutex);
534 heap_free(pContainer->path);
535 heap_free(pContainer->cache_prefix);
536 heap_free(pContainer);
537 }
538
539 void URLCacheContainers_CreateDefaults(void)
540 {
541 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
542 static const WCHAR UrlPrefix[] = {0};
543 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
544 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
545 static const WCHAR CookieSuffix[] = {0};
546 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
547 // ReactOS r50916
548 static const WCHAR UserProfile[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
549 static const struct
550 {
551 int nFolder; /* CSIDL_* constant */
552 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
553 const WCHAR * cache_prefix; /* prefix used to reference the container */
554 } DefaultContainerData[] =
555 {
556 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
557 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
558 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
559 };
560 DWORD i;
561
562 // ReactOS r50916
563 if (GetEnvironmentVariableW(UserProfile, NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND)
564 {
565 TRACE("Environment variable 'USERPROFILE' does not exist!\n");
566 return;
567 }
568
569 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
570 {
571 WCHAR wszCachePath[MAX_PATH];
572 WCHAR wszMutexName[MAX_PATH];
573 int path_len, suffix_len;
574
575 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
576 {
577 ERR("Couldn't get path for default container %u\n", i);
578 continue;
579 }
580 path_len = strlenW(wszCachePath);
581 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
582
583 if (path_len + suffix_len + 2 > MAX_PATH)
584 {
585 ERR("Path too long\n");
586 continue;
587 }
588
589 wszCachePath[path_len] = '\\';
590 wszCachePath[path_len+1] = 0;
591
592 strcpyW(wszMutexName, wszCachePath);
593
594 if (suffix_len)
595 {
596 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
597 wszCachePath[path_len + suffix_len + 1] = '\\';
598 wszCachePath[path_len + suffix_len + 2] = '\0';
599 }
600
601 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
602 }
603 }
604
605 void URLCacheContainers_DeleteAll(void)
606 {
607 while(!list_empty(&UrlContainers))
608 URLCacheContainer_DeleteContainer(
609 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
610 );
611 }
612
613 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
614 {
615 URLCACHECONTAINER * pContainer;
616
617 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
618
619 if(!lpwszUrl)
620 return ERROR_INVALID_PARAMETER;
621
622 // ReactOS r54992
623 if (!bDefaultContainersAdded)
624 URLCacheContainers_CreateDefaults();
625
626 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
627 {
628 int prefix_len = strlenW(pContainer->cache_prefix);
629 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
630 {
631 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
632 *ppContainer = pContainer;
633 return ERROR_SUCCESS;
634 }
635 }
636 ERR("no container found\n");
637 return ERROR_FILE_NOT_FOUND;
638 }
639
640 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
641 {
642 LPWSTR url = NULL;
643 DWORD ret;
644
645 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
646 return ERROR_OUTOFMEMORY;
647
648 ret = URLCacheContainers_FindContainerW(url, ppContainer);
649 heap_free(url);
650 return ret;
651 }
652
653 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
654 {
655 DWORD i = 0;
656 URLCACHECONTAINER * pContainer;
657
658 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
659
660 /* non-NULL search pattern only returns one container ever */
661 if (lpwszSearchPattern && dwIndex > 0)
662 return FALSE;
663
664 // ReactOS r54992
665 if (!bDefaultContainersAdded)
666 URLCacheContainers_CreateDefaults();
667
668 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
669 {
670 if (lpwszSearchPattern)
671 {
672 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
673 {
674 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
675 *ppContainer = pContainer;
676 return TRUE;
677 }
678 }
679 else
680 {
681 if (i == dwIndex)
682 {
683 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
684 *ppContainer = pContainer;
685 return TRUE;
686 }
687 }
688 i++;
689 }
690 return FALSE;
691 }
692
693 /***********************************************************************
694 * URLCacheContainer_LockIndex (Internal)
695 *
696 * Locks the index for system-wide exclusive access.
697 *
698 * RETURNS
699 * Cache file header if successful
700 * NULL if failed and calls SetLastError.
701 */
702 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
703 {
704 BYTE index;
705 LPVOID pIndexData;
706 URLCACHE_HEADER * pHeader;
707 DWORD error;
708
709 /* acquire mutex */
710 WaitForSingleObject(pContainer->hMutex, INFINITE);
711
712 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
713
714 if (!pIndexData)
715 {
716 ReleaseMutex(pContainer->hMutex);
717 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
718 return NULL;
719 }
720 pHeader = (URLCACHE_HEADER *)pIndexData;
721
722 /* file has grown - we need to remap to prevent us getting
723 * access violations when we try and access beyond the end
724 * of the memory mapped file */
725 if (pHeader->dwFileSize != pContainer->file_size)
726 {
727 UnmapViewOfFile( pHeader );
728 URLCacheContainer_CloseIndex(pContainer);
729 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
730 if (error != ERROR_SUCCESS)
731 {
732 ReleaseMutex(pContainer->hMutex);
733 SetLastError(error);
734 return NULL;
735 }
736 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
737
738 if (!pIndexData)
739 {
740 ReleaseMutex(pContainer->hMutex);
741 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
742 return NULL;
743 }
744 pHeader = (URLCACHE_HEADER *)pIndexData;
745 }
746
747 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
748
749 for (index = 0; index < pHeader->DirectoryCount; index++)
750 {
751 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
752 }
753
754 return pHeader;
755 }
756
757 /***********************************************************************
758 * URLCacheContainer_UnlockIndex (Internal)
759 *
760 */
761 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
762 {
763 /* release mutex */
764 ReleaseMutex(pContainer->hMutex);
765 return UnmapViewOfFile(pHeader);
766 }
767
768 /***********************************************************************
769 * URLCacheContainer_CleanIndex (Internal)
770 *
771 * This function is meant to make place in index file by removing old
772 * entries and resizing the file.
773 *
774 * CAUTION: file view may get mapped to new memory
775 * TODO: implement entries cleaning
776 *
777 * RETURNS
778 * ERROR_SUCCESS when new memory is available
779 * error code otherwise
780 */
781 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
782 {
783 URLCACHE_HEADER *header = *file_view;
784 DWORD ret;
785
786 FIXME("(%s %s) semi-stub\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
787
788 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
789 WARN("index file has maximal size\n");
790 return ERROR_NOT_ENOUGH_MEMORY;
791 }
792
793 URLCacheContainer_CloseIndex(container);
794 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
795 if(ret != ERROR_SUCCESS)
796 return ret;
797 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
798 if(!header)
799 return GetLastError();
800
801 UnmapViewOfFile(*file_view);
802 *file_view = header;
803 return ERROR_SUCCESS;
804 }
805
806 #ifndef CHAR_BIT
807 #define CHAR_BIT (8 * sizeof(CHAR))
808 #endif
809
810 /***********************************************************************
811 * URLCache_Allocation_BlockIsFree (Internal)
812 *
813 * Is the specified block number free?
814 *
815 * RETURNS
816 * zero if free
817 * non-zero otherwise
818 *
819 */
820 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
821 {
822 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
823 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
824 }
825
826 /***********************************************************************
827 * URLCache_Allocation_BlockFree (Internal)
828 *
829 * Marks the specified block as free
830 *
831 * RETURNS
832 * nothing
833 *
834 */
835 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
836 {
837 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
838 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
839 }
840
841 /***********************************************************************
842 * URLCache_Allocation_BlockAllocate (Internal)
843 *
844 * Marks the specified block as allocated
845 *
846 * RETURNS
847 * nothing
848 *
849 */
850 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
851 {
852 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
853 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
854 }
855
856 /***********************************************************************
857 * URLCache_FindFirstFreeEntry (Internal)
858 *
859 * Finds and allocates the first block of free space big enough and
860 * sets ppEntry to point to it.
861 *
862 * RETURNS
863 * ERROR_SUCCESS when free memory block was found
864 * Any other Win32 error code if the entry could not be added
865 *
866 */
867 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
868 {
869 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
870 DWORD dwBlockNumber;
871 DWORD dwFreeCounter;
872 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
873 {
874 for (dwFreeCounter = 0;
875 dwFreeCounter < dwBlocksNeeded &&
876 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
877 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
878 dwFreeCounter++)
879 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
880
881 if (dwFreeCounter == dwBlocksNeeded)
882 {
883 DWORD index;
884 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
885 for (index = 0; index < dwBlocksNeeded; index++)
886 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
887 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
888 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
889 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
890 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
891 return ERROR_SUCCESS;
892 }
893 }
894
895 return ERROR_HANDLE_DISK_FULL;
896 }
897
898 /***********************************************************************
899 * URLCache_DeleteEntry (Internal)
900 *
901 * Deletes the specified entry and frees the space allocated to it
902 *
903 * RETURNS
904 * TRUE if it succeeded
905 * FALSE if it failed
906 *
907 */
908 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
909 {
910 DWORD dwStartBlock;
911 DWORD dwBlock;
912 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
913
914 /* update allocation table */
915 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
916 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
917 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
918
919 return TRUE;
920 }
921
922 /***********************************************************************
923 * URLCache_LocalFileNameToPathW (Internal)
924 *
925 * Copies the full path to the specified buffer given the local file
926 * name and the index of the directory it is in. Always sets value in
927 * lpBufferSize to the required buffer size (in bytes).
928 *
929 * RETURNS
930 * TRUE if the buffer was big enough
931 * FALSE if the buffer was too small
932 *
933 */
934 static BOOL URLCache_LocalFileNameToPathW(
935 const URLCACHECONTAINER * pContainer,
936 LPCURLCACHE_HEADER pHeader,
937 LPCSTR szLocalFileName,
938 BYTE Directory,
939 LPWSTR wszPath,
940 LPLONG lpBufferSize)
941 {
942 LONG nRequired;
943 int path_len = strlenW(pContainer->path);
944 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
945 if (Directory >= pHeader->DirectoryCount)
946 {
947 *lpBufferSize = 0;
948 return FALSE;
949 }
950
951 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
952 if (nRequired <= *lpBufferSize)
953 {
954 int dir_len;
955
956 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
957 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
958 wszPath[dir_len + path_len] = '\\';
959 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
960 *lpBufferSize = nRequired;
961 return TRUE;
962 }
963 *lpBufferSize = nRequired;
964 return FALSE;
965 }
966
967 /***********************************************************************
968 * URLCache_LocalFileNameToPathA (Internal)
969 *
970 * Copies the full path to the specified buffer given the local file
971 * name and the index of the directory it is in. Always sets value in
972 * lpBufferSize to the required buffer size.
973 *
974 * RETURNS
975 * TRUE if the buffer was big enough
976 * FALSE if the buffer was too small
977 *
978 */
979 static BOOL URLCache_LocalFileNameToPathA(
980 const URLCACHECONTAINER * pContainer,
981 LPCURLCACHE_HEADER pHeader,
982 LPCSTR szLocalFileName,
983 BYTE Directory,
984 LPSTR szPath,
985 LPLONG lpBufferSize)
986 {
987 LONG nRequired;
988 int path_len, file_name_len, dir_len;
989
990 if (Directory >= pHeader->DirectoryCount)
991 {
992 *lpBufferSize = 0;
993 return FALSE;
994 }
995
996 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
997 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
998 dir_len = DIR_LENGTH;
999
1000 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
1001 if (nRequired < *lpBufferSize)
1002 {
1003 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1004 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
1005 szPath[path_len + dir_len] = '\\';
1006 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
1007 *lpBufferSize = nRequired;
1008 return TRUE;
1009 }
1010 *lpBufferSize = nRequired;
1011 return FALSE;
1012 }
1013
1014 /* Just like DosDateTimeToFileTime, except that it also maps the special
1015 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1016 */
1017 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
1018 FILETIME *ft)
1019 {
1020 if (!fatdate && !fattime)
1021 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1022 else
1023 DosDateTimeToFileTime(fatdate, fattime, ft);
1024 }
1025
1026 /***********************************************************************
1027 * URLCache_CopyEntry (Internal)
1028 *
1029 * Copies an entry from the cache index file to the Win32 structure
1030 *
1031 * RETURNS
1032 * ERROR_SUCCESS if the buffer was big enough
1033 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1034 *
1035 */
1036 static DWORD URLCache_CopyEntry(
1037 URLCACHECONTAINER * pContainer,
1038 LPCURLCACHE_HEADER pHeader,
1039 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1040 LPDWORD lpdwBufferSize,
1041 const URL_CACHEFILE_ENTRY * pUrlEntry,
1042 BOOL bUnicode)
1043 {
1044 int lenUrl;
1045 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1046
1047 if (*lpdwBufferSize >= dwRequiredSize)
1048 {
1049 lpCacheEntryInfo->lpHeaderInfo = NULL;
1050 lpCacheEntryInfo->lpszFileExtension = NULL;
1051 lpCacheEntryInfo->lpszLocalFileName = NULL;
1052 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1053 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1054 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1055 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1056 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1057 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1058 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1059 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1060 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1061 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1062 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1063 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1064 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1065 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1066 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1067 }
1068
1069 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1070 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1071 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1072 if (bUnicode)
1073 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1074 else
1075 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1076 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1077
1078 /* FIXME: is source url optional? */
1079 if (*lpdwBufferSize >= dwRequiredSize)
1080 {
1081 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1082
1083 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1084 if (bUnicode)
1085 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1086 else
1087 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1088 }
1089
1090 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1091 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1092 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1093
1094 if (pUrlEntry->dwOffsetLocalName)
1095 {
1096 LONG nLocalFilePathSize;
1097 LPSTR lpszLocalFileName;
1098 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1099 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1100 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1101 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1102 {
1103 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1104 }
1105 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1106
1107 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1108 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1109 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1110 }
1111 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1112
1113 if (*lpdwBufferSize >= dwRequiredSize)
1114 {
1115 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1116 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1117 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1118 }
1119 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1120 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1121 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1122
1123 if (pUrlEntry->dwOffsetFileExtension)
1124 {
1125 int lenExtension;
1126
1127 if (bUnicode)
1128 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1129 else
1130 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1131 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1132
1133 if (*lpdwBufferSize >= dwRequiredSize)
1134 {
1135 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1136 if (bUnicode)
1137 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1138 else
1139 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1140 }
1141
1142 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1143 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1144 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1145 }
1146
1147 if (dwRequiredSize > *lpdwBufferSize)
1148 {
1149 *lpdwBufferSize = dwRequiredSize;
1150 return ERROR_INSUFFICIENT_BUFFER;
1151 }
1152 *lpdwBufferSize = dwRequiredSize;
1153 return ERROR_SUCCESS;
1154 }
1155
1156 /* Just like FileTimeToDosDateTime, except that it also maps the special
1157 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1158 */
1159 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1160 WORD *fattime)
1161 {
1162 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1163 *fatdate = *fattime = 0;
1164 else
1165 FileTimeToDosDateTime(ft, fatdate, fattime);
1166 }
1167
1168 /***********************************************************************
1169 * URLCache_SetEntryInfo (Internal)
1170 *
1171 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1172 * according to the flags set by dwFieldControl.
1173 *
1174 * RETURNS
1175 * ERROR_SUCCESS if the buffer was big enough
1176 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1177 *
1178 */
1179 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1180 {
1181 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1182 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1183 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1184 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1185 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1186 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1187 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1188 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1189 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1190 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1191 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1192 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1193 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1194 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1195 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1196 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1197
1198 return ERROR_SUCCESS;
1199 }
1200
1201 /***********************************************************************
1202 * URLCache_HashKey (Internal)
1203 *
1204 * Returns the hash key for a given string
1205 *
1206 * RETURNS
1207 * hash key for the string
1208 *
1209 */
1210 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1211 {
1212 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1213 * but the algorithm and result are not the same!
1214 */
1215 static const unsigned char lookupTable[256] =
1216 {
1217 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1218 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1219 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1220 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1221 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1222 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1223 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1224 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1225 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1226 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1227 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1228 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1229 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1230 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1231 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1232 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1233 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1234 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1235 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1236 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1237 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1238 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1239 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1240 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1241 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1242 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1243 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1244 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1245 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1246 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1247 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1248 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1249 };
1250 BYTE key[4];
1251 DWORD i;
1252
1253 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1254 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1255
1256 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1257 {
1258 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1259 key[i] = lookupTable[*lpszKey ^ key[i]];
1260 }
1261
1262 return *(DWORD *)key;
1263 }
1264
1265 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1266 {
1267 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1268 }
1269
1270 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1271 {
1272 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1273 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1274 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1275 }
1276
1277 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1278 {
1279 /* structure of hash table:
1280 * 448 entries divided into 64 blocks
1281 * each block therefore contains a chain of 7 key/offset pairs
1282 * how position in table is calculated:
1283 * 1. the url is hashed in helper function
1284 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1285 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1286 *
1287 * note:
1288 * there can be multiple hash tables in the file and the offset to
1289 * the next one is stored in the header of the hash table
1290 */
1291 DWORD key = URLCache_HashKey(lpszUrl);
1292 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1293 HASH_CACHEFILE_ENTRY * pHashEntry;
1294 DWORD dwHashTableNumber = 0;
1295
1296 key >>= HASHTABLE_FLAG_BITS;
1297
1298 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1299 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1300 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1301 {
1302 int i;
1303 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1304 {
1305 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1306 continue;
1307 }
1308 /* make sure that it is in fact a hash entry */
1309 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1310 {
1311 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1312 continue;
1313 }
1314
1315 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1316 {
1317 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1318 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1319 {
1320 /* FIXME: we should make sure that this is the right element
1321 * before returning and claiming that it is. We can do this
1322 * by doing a simple compare between the URL we were given
1323 * and the URL stored in the entry. However, this assumes
1324 * we know the format of all the entries stored in the
1325 * hash table */
1326 *ppHashEntry = pHashElement;
1327 return TRUE;
1328 }
1329 }
1330 }
1331 return FALSE;
1332 }
1333
1334 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1335 {
1336 LPSTR urlA;
1337 BOOL ret;
1338
1339 urlA = heap_strdupWtoA(lpszUrl);
1340 if (!urlA)
1341 {
1342 SetLastError(ERROR_OUTOFMEMORY);
1343 return FALSE;
1344 }
1345
1346 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1347 heap_free(urlA);
1348 return ret;
1349 }
1350
1351 /***********************************************************************
1352 * URLCache_HashEntrySetFlags (Internal)
1353 *
1354 * Sets special bits in hash key
1355 *
1356 * RETURNS
1357 * nothing
1358 *
1359 */
1360 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1361 {
1362 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1363 }
1364
1365 /***********************************************************************
1366 * URLCache_DeleteEntryFromHash (Internal)
1367 *
1368 * Searches all the hash tables in the index for the given URL and
1369 * then if found deletes the entry.
1370 *
1371 * RETURNS
1372 * TRUE if the entry was found
1373 * FALSE if the entry could not be found
1374 *
1375 */
1376 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1377 {
1378 pHashEntry->dwHashKey = HASHTABLE_DEL;
1379 return TRUE;
1380 }
1381
1382 /***********************************************************************
1383 * URLCache_AddEntryToHash (Internal)
1384 *
1385 * Searches all the hash tables for a free slot based on the offset
1386 * generated from the hash key. If a free slot is found, the offset and
1387 * key are entered into the hash table.
1388 *
1389 * RETURNS
1390 * ERROR_SUCCESS if the entry was added
1391 * Any other Win32 error code if the entry could not be added
1392 *
1393 */
1394 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1395 {
1396 /* see URLCache_FindEntryInHash for structure of hash tables */
1397
1398 DWORD key = URLCache_HashKey(lpszUrl);
1399 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1400 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1401 DWORD dwHashTableNumber = 0;
1402 DWORD error;
1403
1404 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1405
1406 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1407 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1408 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1409 {
1410 int i;
1411 pHashPrev = pHashEntry;
1412
1413 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1414 {
1415 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1416 break;
1417 }
1418 /* make sure that it is in fact a hash entry */
1419 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1420 {
1421 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1422 break;
1423 }
1424
1425 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1426 {
1427 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1428 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1429 {
1430 pHashElement->dwHashKey = key;
1431 pHashElement->dwOffsetEntry = dwOffsetEntry;
1432 return ERROR_SUCCESS;
1433 }
1434 }
1435 }
1436 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1437 if (error != ERROR_SUCCESS)
1438 return error;
1439
1440 pHashEntry->HashTable[offset].dwHashKey = key;
1441 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1442 return ERROR_SUCCESS;
1443 }
1444
1445 /***********************************************************************
1446 * URLCache_CreateHashTable (Internal)
1447 *
1448 * Creates a new hash table in free space and adds it to the chain of existing
1449 * hash tables.
1450 *
1451 * RETURNS
1452 * ERROR_SUCCESS if the hash table was created
1453 * ERROR_DISK_FULL if the hash table could not be created
1454 *
1455 */
1456 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1457 {
1458 DWORD dwOffset, error;
1459 int i;
1460
1461 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1462 return error;
1463
1464 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1465
1466 if (pPrevHash)
1467 pPrevHash->dwAddressNext = dwOffset;
1468 else
1469 pHeader->dwOffsetFirstHashTable = dwOffset;
1470 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1471 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1472 (*ppHash)->dwAddressNext = 0;
1473 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1474 for (i = 0; i < HASHTABLE_SIZE; i++)
1475 {
1476 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1477 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1478 }
1479 return ERROR_SUCCESS;
1480 }
1481
1482 /***********************************************************************
1483 * URLCache_EnumHashTables (Internal)
1484 *
1485 * Enumerates the hash tables in a container.
1486 *
1487 * RETURNS
1488 * TRUE if an entry was found
1489 * FALSE if there are no more tables to enumerate.
1490 *
1491 */
1492 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1493 {
1494 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1495 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1496 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1497 {
1498 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1499 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1500 continue;
1501 /* make sure that it is in fact a hash entry */
1502 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1503 {
1504 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1505 (*pdwHashTableNumber)++;
1506 continue;
1507 }
1508
1509 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1510 return TRUE;
1511 }
1512 return FALSE;
1513 }
1514
1515 /***********************************************************************
1516 * URLCache_EnumHashTableEntries (Internal)
1517 *
1518 * Enumerates entries in a hash table and returns the next non-free entry.
1519 *
1520 * RETURNS
1521 * TRUE if an entry was found
1522 * FALSE if the hash table is empty or there are no more entries to
1523 * enumerate.
1524 *
1525 */
1526 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1527 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1528 {
1529 for (; *index < HASHTABLE_SIZE ; (*index)++)
1530 {
1531 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1532 continue;
1533
1534 *ppHashEntry = &pHashEntry->HashTable[*index];
1535 TRACE("entry found %d\n", *index);
1536 return TRUE;
1537 }
1538 TRACE("no more entries (%d)\n", *index);
1539 return FALSE;
1540 }
1541
1542 /***********************************************************************
1543 * URLCache_DeleteCacheDirectory (Internal)
1544 *
1545 * Erase a directory containing an URL cache.
1546 *
1547 * RETURNS
1548 * TRUE success, FALSE failure/aborted.
1549 *
1550 */
1551 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1552 {
1553 DWORD path_len;
1554 WCHAR path[MAX_PATH + 1];
1555 SHFILEOPSTRUCTW shfos;
1556 int ret;
1557
1558 path_len = strlenW(lpszPath);
1559 if (path_len >= MAX_PATH)
1560 return FALSE;
1561 strcpyW(path, lpszPath);
1562 path[path_len + 1] = 0; /* double-NUL-terminate path */
1563
1564 shfos.hwnd = NULL;
1565 shfos.wFunc = FO_DELETE;
1566 shfos.pFrom = path;
1567 shfos.pTo = NULL;
1568 shfos.fFlags = 0;
1569 shfos.fAnyOperationsAborted = FALSE;
1570 ret = SHFileOperationW(&shfos);
1571 if (ret)
1572 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1573 return !(ret || shfos.fAnyOperationsAborted);
1574 }
1575
1576 /***********************************************************************
1577 * FreeUrlCacheSpaceW (WININET.@)
1578 *
1579 * Frees up some cache.
1580 *
1581 * PARAMETERS
1582 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1583 * dwSize [I] How much space to free up.
1584 * dwSizeType [I] How to interpret dwSize.
1585 *
1586 * RETURNS
1587 * TRUE success. FALSE failure.
1588 *
1589 * IMPLEMENTATION
1590 * This implementation just retrieves the path of the cache directory, and
1591 * deletes its contents from the filesystem. The correct approach would
1592 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1593 */
1594 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1595 {
1596 URLCACHECONTAINER * pContainer;
1597
1598 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1599 {
1600 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1601 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1602 return FALSE;
1603 }
1604
1605 // ReactOS r54992
1606 if (!bDefaultContainersAdded)
1607 URLCacheContainers_CreateDefaults();
1608
1609 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1610 {
1611 /* The URL cache has prefix L"" (unlike Cookies and History) */
1612 if (pContainer->cache_prefix[0] == 0)
1613 {
1614 BOOL ret_del;
1615 DWORD ret_open;
1616 WaitForSingleObject(pContainer->hMutex, INFINITE);
1617
1618 /* unlock, delete, recreate and lock cache */
1619 URLCacheContainer_CloseIndex(pContainer);
1620 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1621 ret_open = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1622
1623 ReleaseMutex(pContainer->hMutex);
1624 return ret_del && (ret_open == ERROR_SUCCESS);
1625 }
1626 }
1627 return FALSE;
1628 }
1629
1630 /***********************************************************************
1631 * FreeUrlCacheSpaceA (WININET.@)
1632 *
1633 * See FreeUrlCacheSpaceW.
1634 */
1635 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1636 {
1637 BOOL ret = FALSE;
1638 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1639 if (lpszCachePath == NULL || path != NULL)
1640 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1641 heap_free(path);
1642 return ret;
1643 }
1644
1645 /***********************************************************************
1646 * GetUrlCacheEntryInfoExA (WININET.@)
1647 *
1648 */
1649 BOOL WINAPI GetUrlCacheEntryInfoExA(
1650 LPCSTR lpszUrl,
1651 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1652 LPDWORD lpdwCacheEntryInfoBufSize,
1653 LPSTR lpszReserved,
1654 LPDWORD lpdwReserved,
1655 LPVOID lpReserved,
1656 DWORD dwFlags)
1657 {
1658 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1659 debugstr_a(lpszUrl),
1660 lpCacheEntryInfo,
1661 lpdwCacheEntryInfoBufSize,
1662 lpszReserved,
1663 lpdwReserved,
1664 lpReserved,
1665 dwFlags);
1666
1667 if ((lpszReserved != NULL) ||
1668 (lpdwReserved != NULL) ||
1669 (lpReserved != NULL))
1670 {
1671 ERR("Reserved value was not 0\n");
1672 SetLastError(ERROR_INVALID_PARAMETER);
1673 return FALSE;
1674 }
1675 if (dwFlags != 0)
1676 {
1677 FIXME("Undocumented flag(s): %x\n", dwFlags);
1678 SetLastError(ERROR_FILE_NOT_FOUND);
1679 return FALSE;
1680 }
1681 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1682 }
1683
1684 /***********************************************************************
1685 * GetUrlCacheEntryInfoA (WININET.@)
1686 *
1687 */
1688 BOOL WINAPI GetUrlCacheEntryInfoA(
1689 IN LPCSTR lpszUrlName,
1690 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1691 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1692 )
1693 {
1694 LPURLCACHE_HEADER pHeader;
1695 struct _HASH_ENTRY * pHashEntry;
1696 const CACHEFILE_ENTRY * pEntry;
1697 const URL_CACHEFILE_ENTRY * pUrlEntry;
1698 URLCACHECONTAINER * pContainer;
1699 DWORD error;
1700
1701 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1702
1703 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1704 if (error != ERROR_SUCCESS)
1705 {
1706 SetLastError(error);
1707 return FALSE;
1708 }
1709
1710 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1711 if (error != ERROR_SUCCESS)
1712 {
1713 SetLastError(error);
1714 return FALSE;
1715 }
1716
1717 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1718 return FALSE;
1719
1720 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1721 {
1722 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1723 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1724 SetLastError(ERROR_FILE_NOT_FOUND);
1725 return FALSE;
1726 }
1727
1728 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1729 if (pEntry->dwSignature != URL_SIGNATURE)
1730 {
1731 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1732 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1733 SetLastError(ERROR_FILE_NOT_FOUND);
1734 return FALSE;
1735 }
1736
1737 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1738 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1739 if (pUrlEntry->dwOffsetHeaderInfo)
1740 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1741
1742 if (lpdwCacheEntryInfoBufferSize)
1743 {
1744 if (!lpCacheEntryInfo)
1745 *lpdwCacheEntryInfoBufferSize = 0;
1746
1747 error = URLCache_CopyEntry(
1748 pContainer,
1749 pHeader,
1750 lpCacheEntryInfo,
1751 lpdwCacheEntryInfoBufferSize,
1752 pUrlEntry,
1753 FALSE /* ANSI */);
1754 if (error != ERROR_SUCCESS)
1755 {
1756 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1757 SetLastError(error);
1758 return FALSE;
1759 }
1760 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1761 }
1762
1763 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1764
1765 return TRUE;
1766 }
1767
1768 /***********************************************************************
1769 * GetUrlCacheEntryInfoW (WININET.@)
1770 *
1771 */
1772 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1773 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1774 LPDWORD lpdwCacheEntryInfoBufferSize)
1775 {
1776 LPURLCACHE_HEADER pHeader;
1777 struct _HASH_ENTRY * pHashEntry;
1778 const CACHEFILE_ENTRY * pEntry;
1779 const URL_CACHEFILE_ENTRY * pUrlEntry;
1780 URLCACHECONTAINER * pContainer;
1781 DWORD error;
1782
1783 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1784
1785 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1786 if (error != ERROR_SUCCESS)
1787 {
1788 SetLastError(error);
1789 return FALSE;
1790 }
1791
1792 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1793 if (error != ERROR_SUCCESS)
1794 {
1795 SetLastError(error);
1796 return FALSE;
1797 }
1798
1799 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1800 return FALSE;
1801
1802 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1803 {
1804 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1805 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1806 SetLastError(ERROR_FILE_NOT_FOUND);
1807 return FALSE;
1808 }
1809
1810 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1811 if (pEntry->dwSignature != URL_SIGNATURE)
1812 {
1813 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1814 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1815 SetLastError(ERROR_FILE_NOT_FOUND);
1816 return FALSE;
1817 }
1818
1819 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1820 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1821 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1822
1823 if (lpdwCacheEntryInfoBufferSize)
1824 {
1825 if (!lpCacheEntryInfo)
1826 *lpdwCacheEntryInfoBufferSize = 0;
1827
1828 error = URLCache_CopyEntry(
1829 pContainer,
1830 pHeader,
1831 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1832 lpdwCacheEntryInfoBufferSize,
1833 pUrlEntry,
1834 TRUE /* UNICODE */);
1835 if (error != ERROR_SUCCESS)
1836 {
1837 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1838 SetLastError(error);
1839 return FALSE;
1840 }
1841 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1842 }
1843
1844 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1845
1846 return TRUE;
1847 }
1848
1849 /***********************************************************************
1850 * GetUrlCacheEntryInfoExW (WININET.@)
1851 *
1852 */
1853 BOOL WINAPI GetUrlCacheEntryInfoExW(
1854 LPCWSTR lpszUrl,
1855 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1856 LPDWORD lpdwCacheEntryInfoBufSize,
1857 LPWSTR lpszReserved,
1858 LPDWORD lpdwReserved,
1859 LPVOID lpReserved,
1860 DWORD dwFlags)
1861 {
1862 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1863 debugstr_w(lpszUrl),
1864 lpCacheEntryInfo,
1865 lpdwCacheEntryInfoBufSize,
1866 lpszReserved,
1867 lpdwReserved,
1868 lpReserved,
1869 dwFlags);
1870
1871 if ((lpszReserved != NULL) ||
1872 (lpdwReserved != NULL) ||
1873 (lpReserved != NULL))
1874 {
1875 ERR("Reserved value was not 0\n");
1876 SetLastError(ERROR_INVALID_PARAMETER);
1877 return FALSE;
1878 }
1879 if (dwFlags != 0)
1880 {
1881 FIXME("Undocumented flag(s): %x\n", dwFlags);
1882 SetLastError(ERROR_FILE_NOT_FOUND);
1883 return FALSE;
1884 }
1885 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1886 }
1887
1888 /***********************************************************************
1889 * SetUrlCacheEntryInfoA (WININET.@)
1890 */
1891 BOOL WINAPI SetUrlCacheEntryInfoA(
1892 LPCSTR lpszUrlName,
1893 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1894 DWORD dwFieldControl)
1895 {
1896 LPURLCACHE_HEADER pHeader;
1897 struct _HASH_ENTRY * pHashEntry;
1898 CACHEFILE_ENTRY * pEntry;
1899 URLCACHECONTAINER * pContainer;
1900 DWORD error;
1901
1902 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1903
1904 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1905 if (error != ERROR_SUCCESS)
1906 {
1907 SetLastError(error);
1908 return FALSE;
1909 }
1910
1911 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1912 if (error != ERROR_SUCCESS)
1913 {
1914 SetLastError(error);
1915 return FALSE;
1916 }
1917
1918 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1919 return FALSE;
1920
1921 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1922 {
1923 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1924 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1925 SetLastError(ERROR_FILE_NOT_FOUND);
1926 return FALSE;
1927 }
1928
1929 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1930 if (pEntry->dwSignature != URL_SIGNATURE)
1931 {
1932 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1933 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1934 SetLastError(ERROR_FILE_NOT_FOUND);
1935 return FALSE;
1936 }
1937
1938 URLCache_SetEntryInfo(
1939 (URL_CACHEFILE_ENTRY *)pEntry,
1940 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1941 dwFieldControl);
1942
1943 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1944
1945 return TRUE;
1946 }
1947
1948 /***********************************************************************
1949 * SetUrlCacheEntryInfoW (WININET.@)
1950 */
1951 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1952 {
1953 LPURLCACHE_HEADER pHeader;
1954 struct _HASH_ENTRY * pHashEntry;
1955 CACHEFILE_ENTRY * pEntry;
1956 URLCACHECONTAINER * pContainer;
1957 DWORD error;
1958
1959 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1960
1961 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1962 if (error != ERROR_SUCCESS)
1963 {
1964 SetLastError(error);
1965 return FALSE;
1966 }
1967
1968 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1969 if (error != ERROR_SUCCESS)
1970 {
1971 SetLastError(error);
1972 return FALSE;
1973 }
1974
1975 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1976 return FALSE;
1977
1978 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1979 {
1980 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1981 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1982 SetLastError(ERROR_FILE_NOT_FOUND);
1983 return FALSE;
1984 }
1985
1986 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1987 if (pEntry->dwSignature != URL_SIGNATURE)
1988 {
1989 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1990 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1991 SetLastError(ERROR_FILE_NOT_FOUND);
1992 return FALSE;
1993 }
1994
1995 URLCache_SetEntryInfo(
1996 (URL_CACHEFILE_ENTRY *)pEntry,
1997 lpCacheEntryInfo,
1998 dwFieldControl);
1999
2000 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2001
2002 return TRUE;
2003 }
2004
2005 /***********************************************************************
2006 * RetrieveUrlCacheEntryFileA (WININET.@)
2007 *
2008 */
2009 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2010 IN LPCSTR lpszUrlName,
2011 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2012 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2013 IN DWORD dwReserved
2014 )
2015 {
2016 LPURLCACHE_HEADER pHeader;
2017 struct _HASH_ENTRY * pHashEntry;
2018 CACHEFILE_ENTRY * pEntry;
2019 URL_CACHEFILE_ENTRY * pUrlEntry;
2020 URLCACHECONTAINER * pContainer;
2021 DWORD error;
2022
2023 TRACE("(%s, %p, %p, 0x%08x)\n",
2024 debugstr_a(lpszUrlName),
2025 lpCacheEntryInfo,
2026 lpdwCacheEntryInfoBufferSize,
2027 dwReserved);
2028
2029 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2030 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2031 {
2032 SetLastError(ERROR_INVALID_PARAMETER);
2033 return FALSE;
2034 }
2035
2036 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2037 if (error != ERROR_SUCCESS)
2038 {
2039 SetLastError(error);
2040 return FALSE;
2041 }
2042
2043 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2044 if (error != ERROR_SUCCESS)
2045 {
2046 SetLastError(error);
2047 return FALSE;
2048 }
2049
2050 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2051 return FALSE;
2052
2053 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2054 {
2055 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2056 TRACE("entry %s not found!\n", lpszUrlName);
2057 SetLastError(ERROR_FILE_NOT_FOUND);
2058 return FALSE;
2059 }
2060
2061 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2062 if (pEntry->dwSignature != URL_SIGNATURE)
2063 {
2064 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2065 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2066 SetLastError(ERROR_FILE_NOT_FOUND);
2067 return FALSE;
2068 }
2069
2070 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2071 if (!pUrlEntry->dwOffsetLocalName)
2072 {
2073 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2074 SetLastError(ERROR_INVALID_DATA);
2075 return FALSE;
2076 }
2077
2078 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2079 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2080
2081 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2082 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2083 FALSE);
2084 if (error != ERROR_SUCCESS)
2085 {
2086 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2087 SetLastError(error);
2088 return FALSE;
2089 }
2090 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2091
2092 pUrlEntry->dwHitRate++;
2093 pUrlEntry->dwUseCount++;
2094 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2095 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2096
2097 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2098
2099 return TRUE;
2100 }
2101
2102 /***********************************************************************
2103 * RetrieveUrlCacheEntryFileW (WININET.@)
2104 *
2105 */
2106 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2107 IN LPCWSTR lpszUrlName,
2108 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2109 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2110 IN DWORD dwReserved
2111 )
2112 {
2113 LPURLCACHE_HEADER pHeader;
2114 struct _HASH_ENTRY * pHashEntry;
2115 CACHEFILE_ENTRY * pEntry;
2116 URL_CACHEFILE_ENTRY * pUrlEntry;
2117 URLCACHECONTAINER * pContainer;
2118 DWORD error;
2119
2120 TRACE("(%s, %p, %p, 0x%08x)\n",
2121 debugstr_w(lpszUrlName),
2122 lpCacheEntryInfo,
2123 lpdwCacheEntryInfoBufferSize,
2124 dwReserved);
2125
2126 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2127 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2128 {
2129 SetLastError(ERROR_INVALID_PARAMETER);
2130 return FALSE;
2131 }
2132
2133 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2134 if (error != ERROR_SUCCESS)
2135 {
2136 SetLastError(error);
2137 return FALSE;
2138 }
2139
2140 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2141 if (error != ERROR_SUCCESS)
2142 {
2143 SetLastError(error);
2144 return FALSE;
2145 }
2146
2147 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2148 return FALSE;
2149
2150 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2151 {
2152 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2153 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2154 SetLastError(ERROR_FILE_NOT_FOUND);
2155 return FALSE;
2156 }
2157
2158 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2159 if (pEntry->dwSignature != URL_SIGNATURE)
2160 {
2161 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2162 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2163 SetLastError(ERROR_FILE_NOT_FOUND);
2164 return FALSE;
2165 }
2166
2167 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2168 if (!pUrlEntry->dwOffsetLocalName)
2169 {
2170 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2171 SetLastError(ERROR_INVALID_DATA);
2172 return FALSE;
2173 }
2174
2175 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2176 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2177
2178 error = URLCache_CopyEntry(
2179 pContainer,
2180 pHeader,
2181 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2182 lpdwCacheEntryInfoBufferSize,
2183 pUrlEntry,
2184 TRUE /* UNICODE */);
2185 if (error != ERROR_SUCCESS)
2186 {
2187 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2188 SetLastError(error);
2189 return FALSE;
2190 }
2191 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2192
2193 pUrlEntry->dwHitRate++;
2194 pUrlEntry->dwUseCount++;
2195 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2196 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2197
2198 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2199
2200 return TRUE;
2201 }
2202
2203 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2204 struct _HASH_ENTRY *pHashEntry)
2205 {
2206 CACHEFILE_ENTRY * pEntry;
2207 URL_CACHEFILE_ENTRY * pUrlEntry;
2208
2209 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2210 if (pEntry->dwSignature != URL_SIGNATURE)
2211 {
2212 FIXME("Trying to delete entry of unknown format %s\n",
2213 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2214 SetLastError(ERROR_FILE_NOT_FOUND);
2215 return FALSE;
2216 }
2217
2218 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2219 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2220 {
2221 /* FIXME: implement timeout object unlocking */
2222 TRACE("Trying to delete locked entry\n");
2223 pUrlEntry->CacheEntryType |= DELETED_CACHE_ENTRY;
2224 SetLastError(ERROR_SHARING_VIOLATION);
2225 return FALSE;
2226 }
2227
2228 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2229 {
2230 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2231 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2232 }
2233 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2234 {
2235 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2236 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2237 else
2238 pHeader->ExemptUsage.QuadPart = 0;
2239 }
2240 else
2241 {
2242 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2243 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2244 else
2245 pHeader->CacheUsage.QuadPart = 0;
2246 }
2247
2248 URLCache_DeleteEntry(pHeader, pEntry);
2249
2250 URLCache_DeleteEntryFromHash(pHashEntry);
2251 return TRUE;
2252 }
2253
2254 /***********************************************************************
2255 * UnlockUrlCacheEntryFileA (WININET.@)
2256 *
2257 */
2258 BOOL WINAPI UnlockUrlCacheEntryFileA(
2259 IN LPCSTR lpszUrlName,
2260 IN DWORD dwReserved
2261 )
2262 {
2263 LPURLCACHE_HEADER pHeader;
2264 struct _HASH_ENTRY * pHashEntry;
2265 CACHEFILE_ENTRY * pEntry;
2266 URL_CACHEFILE_ENTRY * pUrlEntry;
2267 URLCACHECONTAINER * pContainer;
2268 DWORD error;
2269
2270 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2271
2272 if (dwReserved)
2273 {
2274 ERR("dwReserved != 0\n");
2275 SetLastError(ERROR_INVALID_PARAMETER);
2276 return FALSE;
2277 }
2278
2279 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2280 if (error != ERROR_SUCCESS)
2281 {
2282 SetLastError(error);
2283 return FALSE;
2284 }
2285
2286 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2287 if (error != ERROR_SUCCESS)
2288 {
2289 SetLastError(error);
2290 return FALSE;
2291 }
2292
2293 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2294 return FALSE;
2295
2296 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2297 {
2298 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2299 TRACE("entry %s not found!\n", lpszUrlName);
2300 SetLastError(ERROR_FILE_NOT_FOUND);
2301 return FALSE;
2302 }
2303
2304 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2305 if (pEntry->dwSignature != URL_SIGNATURE)
2306 {
2307 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2308 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2309 SetLastError(ERROR_FILE_NOT_FOUND);
2310 return FALSE;
2311 }
2312
2313 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2314
2315 if (pUrlEntry->dwUseCount == 0)
2316 {
2317 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2318 return FALSE;
2319 }
2320 pUrlEntry->dwUseCount--;
2321 if (!pUrlEntry->dwUseCount)
2322 {
2323 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2324 if (pUrlEntry->CacheEntryType & DELETED_CACHE_ENTRY)
2325 DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
2326 }
2327
2328 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2329
2330 return TRUE;
2331 }
2332
2333 /***********************************************************************
2334 * UnlockUrlCacheEntryFileW (WININET.@)
2335 *
2336 */
2337 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2338 {
2339 LPURLCACHE_HEADER pHeader;
2340 struct _HASH_ENTRY * pHashEntry;
2341 CACHEFILE_ENTRY * pEntry;
2342 URL_CACHEFILE_ENTRY * pUrlEntry;
2343 URLCACHECONTAINER * pContainer;
2344 DWORD error;
2345
2346 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2347
2348 if (dwReserved)
2349 {
2350 ERR("dwReserved != 0\n");
2351 SetLastError(ERROR_INVALID_PARAMETER);
2352 return FALSE;
2353 }
2354
2355 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2356 if (error != ERROR_SUCCESS)
2357 {
2358 SetLastError(error);
2359 return FALSE;
2360 }
2361
2362 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2363 if (error != ERROR_SUCCESS)
2364 {
2365 SetLastError(error);
2366 return FALSE;
2367 }
2368
2369 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2370 return FALSE;
2371
2372 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2373 {
2374 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2375 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2376 SetLastError(ERROR_FILE_NOT_FOUND);
2377 return FALSE;
2378 }
2379
2380 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2381 if (pEntry->dwSignature != URL_SIGNATURE)
2382 {
2383 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2384 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2385 SetLastError(ERROR_FILE_NOT_FOUND);
2386 return FALSE;
2387 }
2388
2389 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2390
2391 if (pUrlEntry->dwUseCount == 0)
2392 {
2393 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2394 return FALSE;
2395 }
2396 pUrlEntry->dwUseCount--;
2397 if (!pUrlEntry->dwUseCount)
2398 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2399
2400 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2401
2402 return TRUE;
2403 }
2404
2405 /***********************************************************************
2406 * CreateUrlCacheEntryA (WININET.@)
2407 *
2408 */
2409 BOOL WINAPI CreateUrlCacheEntryA(
2410 IN LPCSTR lpszUrlName,
2411 IN DWORD dwExpectedFileSize,
2412 IN LPCSTR lpszFileExtension,
2413 OUT LPSTR lpszFileName,
2414 IN DWORD dwReserved
2415 )
2416 {
2417 WCHAR *url_name;
2418 WCHAR *file_extension = NULL;
2419 WCHAR file_name[MAX_PATH];
2420 BOOL bSuccess = FALSE;
2421 DWORD dwError = 0;
2422
2423 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2424 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2425
2426 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2427 {
2428 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2429 {
2430 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2431 {
2432 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2433 {
2434 bSuccess = TRUE;
2435 }
2436 else
2437 {
2438 dwError = GetLastError();
2439 }
2440 }
2441 else
2442 {
2443 dwError = GetLastError();
2444 }
2445 heap_free(file_extension);
2446 }
2447 else
2448 {
2449 dwError = GetLastError();
2450 }
2451 heap_free(url_name);
2452 if (!bSuccess) SetLastError(dwError);
2453 }
2454 return bSuccess;
2455 }
2456 /***********************************************************************
2457 * CreateUrlCacheEntryW (WININET.@)
2458 *
2459 */
2460 BOOL WINAPI CreateUrlCacheEntryW(
2461 IN LPCWSTR lpszUrlName,
2462 IN DWORD dwExpectedFileSize,
2463 IN LPCWSTR lpszFileExtension,
2464 OUT LPWSTR lpszFileName,
2465 IN DWORD dwReserved
2466 )
2467 {
2468 URLCACHECONTAINER * pContainer;
2469 LPURLCACHE_HEADER pHeader;
2470 CHAR szFile[MAX_PATH];
2471 WCHAR szExtension[MAX_PATH];
2472 LPCWSTR lpszUrlPart;
2473 LPCWSTR lpszUrlEnd;
2474 LPCWSTR lpszFileNameExtension;
2475 LPWSTR lpszFileNameNoPath;
2476 int i;
2477 int countnoextension;
2478 BYTE CacheDir;
2479 LONG lBufferSize;
2480 BOOL bFound = FALSE;
2481 int count;
2482 DWORD error;
2483 HANDLE hFile;
2484 FILETIME ft;
2485
2486 static const WCHAR szWWW[] = {'w','w','w',0};
2487 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2488
2489 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2490 debugstr_w(lpszUrlName),
2491 dwExpectedFileSize,
2492 debugstr_w(lpszFileExtension),
2493 lpszFileName,
2494 dwReserved);
2495
2496 if (dwReserved)
2497 FIXME("dwReserved 0x%08x\n", dwReserved);
2498
2499 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2500
2501 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2502 lpszUrlEnd--;
2503
2504 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2505 if (!lpszUrlPart)
2506 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2507 if (lpszUrlPart)
2508 lpszUrlEnd = lpszUrlPart;
2509
2510 for (lpszUrlPart = lpszUrlEnd;
2511 (lpszUrlPart >= lpszUrlName);
2512 lpszUrlPart--)
2513 {
2514 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2515 {
2516 bFound = TRUE;
2517 lpszUrlPart++;
2518 break;
2519 }
2520 }
2521 if (!lstrcmpW(lpszUrlPart, szWWW))
2522 {
2523 lpszUrlPart += lstrlenW(szWWW);
2524 }
2525
2526 count = lpszUrlEnd - lpszUrlPart;
2527
2528 if (bFound && (count < MAX_PATH))
2529 {
2530 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2531 if (!len)
2532 return FALSE;
2533 szFile[len] = '\0';
2534 while(len && szFile[--len] == '/') szFile[len] = '\0';
2535
2536 /* FIXME: get rid of illegal characters like \, / and : */
2537 }
2538 else
2539 {
2540 FIXME("need to generate a random filename\n");
2541 }
2542
2543 TRACE("File name: %s\n", debugstr_a(szFile));
2544
2545 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2546 if (error != ERROR_SUCCESS)
2547 {
2548 SetLastError(error);
2549 return FALSE;
2550 }
2551
2552 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2553 if (error != ERROR_SUCCESS)
2554 {
2555 SetLastError(error);
2556 return FALSE;
2557 }
2558
2559 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2560 return FALSE;
2561
2562 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2563
2564 lBufferSize = MAX_PATH * sizeof(WCHAR);
2565 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2566 {
2567 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2568 debugstr_a(szFile), lBufferSize);
2569 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2570 return FALSE;
2571 }
2572
2573 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2574
2575 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2576 lpszFileNameNoPath >= lpszFileName;
2577 --lpszFileNameNoPath)
2578 {
2579 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2580 break;
2581 }
2582
2583 countnoextension = lstrlenW(lpszFileNameNoPath);
2584 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2585 if (lpszFileNameExtension)
2586 countnoextension -= lstrlenW(lpszFileNameExtension);
2587 *szExtension = '\0';
2588
2589 if (lpszFileExtension)
2590 {
2591 szExtension[0] = '.';
2592 lstrcpyW(szExtension+1, lpszFileExtension);
2593 }
2594
2595 for (i = 0; i < 255; i++)
2596 {
2597 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2598 WCHAR *p;
2599
2600 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2601 for (p = lpszFileNameNoPath + 1; *p; p++)
2602 {
2603 switch (*p)
2604 {
2605 case '<': case '>':
2606 case ':': case '"':
2607 case '/': case '\\':
2608 case '|': case '?':
2609 case '*':
2610 *p = '_'; break;
2611 default: break;
2612 }
2613 }
2614 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2615
2616 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2617 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2618 if (hFile != INVALID_HANDLE_VALUE)
2619 {
2620 CloseHandle(hFile);
2621 return TRUE;
2622 }
2623 }
2624
2625 GetSystemTimeAsFileTime(&ft);
2626 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2627
2628 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2629 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2630 if (hFile != INVALID_HANDLE_VALUE)
2631 {
2632 CloseHandle(hFile);
2633 return TRUE;
2634 }
2635
2636 WARN("Could not find a unique filename\n");
2637 return FALSE;
2638 }
2639
2640
2641 /***********************************************************************
2642 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2643 *
2644 * The bug we are compensating for is that some drongo at Microsoft
2645 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2646 * As a consequence, CommitUrlCacheEntryA has been effectively
2647 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2648 * is still defined as LPCWSTR. The result (other than madness) is
2649 * that we always need to store lpHeaderInfo in CP_ACP rather than
2650 * in UTF16, and we need to avoid converting lpHeaderInfo in
2651 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2652 * result will lose data for arbitrary binary data.
2653 *
2654 */
2655 static BOOL CommitUrlCacheEntryInternal(
2656 IN LPCWSTR lpszUrlName,
2657 IN LPCWSTR lpszLocalFileName,
2658 IN FILETIME ExpireTime,
2659 IN FILETIME LastModifiedTime,
2660 IN DWORD CacheEntryType,
2661 IN LPBYTE lpHeaderInfo,
2662 IN DWORD dwHeaderSize,
2663 IN LPCWSTR lpszFileExtension,
2664 IN LPCWSTR lpszOriginalUrl
2665 )
2666 {
2667 URLCACHECONTAINER * pContainer;
2668 LPURLCACHE_HEADER pHeader;
2669 struct _HASH_ENTRY * pHashEntry;
2670 CACHEFILE_ENTRY * pEntry;
2671 URL_CACHEFILE_ENTRY * pUrlEntry;
2672 DWORD url_entry_offset;
2673 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2674 DWORD dwOffsetLocalFileName = 0;
2675 DWORD dwOffsetHeader = 0;
2676 DWORD dwOffsetFileExtension = 0;
2677 LARGE_INTEGER file_size;
2678 BYTE cDirectory = 0;
2679 char achFile[MAX_PATH];
2680 LPSTR lpszUrlNameA = NULL;
2681 LPSTR lpszFileExtensionA = NULL;
2682 char *pchLocalFileName = 0;
2683 DWORD error;
2684
2685 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2686 debugstr_w(lpszUrlName),
2687 debugstr_w(lpszLocalFileName),
2688 CacheEntryType,
2689 lpHeaderInfo,
2690 dwHeaderSize,
2691 debugstr_w(lpszFileExtension),
2692 debugstr_w(lpszOriginalUrl));
2693
2694 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2695 {
2696 SetLastError(ERROR_INVALID_PARAMETER);
2697 return FALSE;
2698 }
2699 if (lpszOriginalUrl)
2700 WARN(": lpszOriginalUrl ignored\n");
2701
2702 file_size.QuadPart = 0;
2703 if (lpszLocalFileName)
2704 {
2705 HANDLE hFile;
2706
2707 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2708 if (hFile == INVALID_HANDLE_VALUE)
2709 {
2710 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2711 return FALSE;
2712 }
2713
2714 /* Get file size */
2715 if (!GetFileSizeEx(hFile, &file_size))
2716 {
2717 ERR("couldn't get file size (error is %d)\n", GetLastError());
2718 CloseHandle(hFile);
2719 return FALSE;
2720 }
2721
2722 CloseHandle(hFile);
2723 }
2724
2725 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2726 if (error != ERROR_SUCCESS)
2727 {
2728 SetLastError(error);
2729 return FALSE;
2730 }
2731
2732 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2733 if (error != ERROR_SUCCESS)
2734 {
2735 SetLastError(error);
2736 return FALSE;
2737 }
2738
2739 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2740 return FALSE;
2741
2742 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2743 if (!lpszUrlNameA)
2744 {
2745 error = GetLastError();
2746 goto cleanup;
2747 }
2748
2749 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2750 {
2751 error = GetLastError();
2752 goto cleanup;
2753 }
2754
2755 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2756 {
2757 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2758 {
2759 /* FIXME: implement timeout object unlocking */
2760 FIXME("Trying to overwrite locked entry\n");
2761 SetLastError(ERROR_SHARING_VIOLATION);
2762 goto cleanup;
2763 }
2764
2765 FIXME("entry already in cache - don't know what to do!\n");
2766 /*
2767 * SetLastError(ERROR_FILE_NOT_FOUND);
2768 * return FALSE;
2769 */
2770 goto cleanup;
2771 }
2772
2773 if (lpszLocalFileName)
2774 {
2775 BOOL bFound = FALSE;
2776
2777 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2778 {
2779 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2780 error = ERROR_INVALID_PARAMETER;
2781 goto cleanup;
2782 }
2783
2784 /* skip container path prefix */
2785 lpszLocalFileName += lstrlenW(pContainer->path);
2786
2787 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2788 pchLocalFileName = achFile;
2789
2790 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2791 {
2792 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2793 {
2794 bFound = TRUE;
2795 break;
2796 }
2797 }
2798
2799 if (!bFound)
2800 {
2801 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2802 error = ERROR_INVALID_PARAMETER;
2803 goto cleanup;
2804 }
2805
2806 lpszLocalFileName += DIR_LENGTH + 1;
2807 pchLocalFileName += DIR_LENGTH + 1;
2808 }
2809
2810 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2811 if (lpszLocalFileName)
2812 {
2813 dwOffsetLocalFileName = dwBytesNeeded;
2814 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2815 }
2816 if (lpHeaderInfo)
2817 {
2818 dwOffsetHeader = dwBytesNeeded;
2819 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2820 }
2821 if (lpszFileExtensionA)
2822 {
2823 dwOffsetFileExtension = dwBytesNeeded;
2824 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2825 }
2826
2827 /* round up to next block */
2828 if (dwBytesNeeded % BLOCKSIZE)
2829 {
2830 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2831 dwBytesNeeded += BLOCKSIZE;
2832 }
2833
2834 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2835 while (error == ERROR_HANDLE_DISK_FULL)
2836 {
2837 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2838 if (error == ERROR_SUCCESS)
2839 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2840 }
2841 if (error != ERROR_SUCCESS)
2842 goto cleanup;
2843
2844 /* FindFirstFreeEntry fills in blocks used */
2845 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2846 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
2847 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2848 pUrlEntry->CacheDir = cDirectory;
2849 pUrlEntry->CacheEntryType = CacheEntryType;
2850 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2851 if (CacheEntryType & STICKY_CACHE_ENTRY)
2852 {
2853 /* Sticky entries have a default exempt time of one day */
2854 pUrlEntry->dwExemptDelta = 86400;
2855 }
2856 else
2857 pUrlEntry->dwExemptDelta = 0;
2858 pUrlEntry->dwHitRate = 0;
2859 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2860 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2861 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2862 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2863 pUrlEntry->size.QuadPart = file_size.QuadPart;
2864 pUrlEntry->dwUseCount = 0;
2865 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2866 pUrlEntry->LastModifiedTime = LastModifiedTime;
2867 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2868 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2869 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2870 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2871
2872 /*** Unknowns ***/
2873 pUrlEntry->dwUnknown1 = 0;
2874 pUrlEntry->dwUnknown2 = 0;
2875 pUrlEntry->dwUnknown3 = 0x60;
2876 pUrlEntry->Unknown4 = 0;
2877 pUrlEntry->wUnknown5 = 0x1010;
2878 pUrlEntry->dwUnknown7 = 0;
2879 pUrlEntry->dwUnknown8 = 0;
2880
2881
2882 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2883 if (dwOffsetLocalFileName)
2884 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
2885 if (dwOffsetHeader)
2886 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2887 if (dwOffsetFileExtension)
2888 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2889
2890 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
2891 while (error == ERROR_HANDLE_DISK_FULL)
2892 {
2893 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2894 if (error == ERROR_SUCCESS)
2895 {
2896 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
2897 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2898 url_entry_offset, HASHTABLE_URL);
2899 }
2900 }
2901 if (error != ERROR_SUCCESS)
2902 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2903 else
2904 {
2905 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2906 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2907 if (CacheEntryType & STICKY_CACHE_ENTRY)
2908 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2909 else
2910 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2911 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2912 pHeader->CacheLimit.QuadPart)
2913 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2914 }
2915
2916 cleanup:
2917 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2918 heap_free(lpszUrlNameA);
2919 heap_free(lpszFileExtensionA);
2920
2921 if (error == ERROR_SUCCESS)
2922 return TRUE;
2923 else
2924 {
2925 SetLastError(error);
2926 return FALSE;
2927 }
2928 }
2929
2930 /***********************************************************************
2931 * CommitUrlCacheEntryA (WININET.@)
2932 *
2933 */
2934 BOOL WINAPI CommitUrlCacheEntryA(
2935 IN LPCSTR lpszUrlName,
2936 IN LPCSTR lpszLocalFileName,
2937 IN FILETIME ExpireTime,
2938 IN FILETIME LastModifiedTime,
2939 IN DWORD CacheEntryType,
2940 IN LPBYTE lpHeaderInfo,
2941 IN DWORD dwHeaderSize,
2942 IN LPCSTR lpszFileExtension,
2943 IN LPCSTR lpszOriginalUrl
2944 )
2945 {
2946 WCHAR *url_name = NULL;
2947 WCHAR *local_file_name = NULL;
2948 WCHAR *original_url = NULL;
2949 WCHAR *file_extension = NULL;
2950 BOOL bSuccess = FALSE;
2951
2952 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2953 debugstr_a(lpszUrlName),
2954 debugstr_a(lpszLocalFileName),
2955 CacheEntryType,
2956 lpHeaderInfo,
2957 dwHeaderSize,
2958 debugstr_a(lpszFileExtension),
2959 debugstr_a(lpszOriginalUrl));
2960
2961 url_name = heap_strdupAtoW(lpszUrlName);
2962 if (!url_name)
2963 goto cleanup;
2964
2965 if (lpszLocalFileName)
2966 {
2967 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2968 if (!local_file_name)
2969 goto cleanup;
2970 }
2971 if (lpszFileExtension)
2972 {
2973 file_extension = heap_strdupAtoW(lpszFileExtension);
2974 if (!file_extension)
2975 goto cleanup;
2976 }
2977 if (lpszOriginalUrl)
2978 {
2979 original_url = heap_strdupAtoW(lpszOriginalUrl);
2980 if (!original_url)
2981 goto cleanup;
2982 }
2983
2984 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2985 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2986 file_extension, original_url);
2987
2988 cleanup:
2989 heap_free(original_url);
2990 heap_free(file_extension);
2991 heap_free(local_file_name);
2992 heap_free(url_name);
2993 return bSuccess;
2994 }
2995
2996 /***********************************************************************
2997 * CommitUrlCacheEntryW (WININET.@)
2998 *
2999 */
3000 BOOL WINAPI CommitUrlCacheEntryW(
3001 IN LPCWSTR lpszUrlName,
3002 IN LPCWSTR lpszLocalFileName,
3003 IN FILETIME ExpireTime,
3004 IN FILETIME LastModifiedTime,
3005 IN DWORD CacheEntryType,
3006 IN LPWSTR lpHeaderInfo,
3007 IN DWORD dwHeaderSize,
3008 IN LPCWSTR lpszFileExtension,
3009 IN LPCWSTR lpszOriginalUrl
3010 )
3011 {
3012 DWORD dwError = 0;
3013 BOOL bSuccess = FALSE;
3014 DWORD len = 0;
3015 CHAR *header_info = NULL;
3016
3017 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3018 debugstr_w(lpszUrlName),
3019 debugstr_w(lpszLocalFileName),
3020 CacheEntryType,
3021 lpHeaderInfo,
3022 dwHeaderSize,
3023 debugstr_w(lpszFileExtension),
3024 debugstr_w(lpszOriginalUrl));
3025
3026 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3027 {
3028 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3029 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3030 {
3031 bSuccess = TRUE;
3032 }
3033 else
3034 {
3035 dwError = GetLastError();
3036 }
3037 if (header_info)
3038 {
3039 heap_free(header_info);
3040 if (!bSuccess)
3041 SetLastError(dwError);
3042 }
3043 }
3044 return bSuccess;
3045 }
3046
3047 /***********************************************************************
3048 * ReadUrlCacheEntryStream (WININET.@)
3049 *
3050 */
3051 BOOL WINAPI ReadUrlCacheEntryStream(
3052 IN HANDLE hUrlCacheStream,
3053 IN DWORD dwLocation,
3054 IN OUT LPVOID lpBuffer,
3055 IN OUT LPDWORD lpdwLen,
3056 IN DWORD dwReserved
3057 )
3058 {
3059 /* Get handle to file from 'stream' */
3060 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3061
3062 if (dwReserved != 0)
3063 {
3064 ERR("dwReserved != 0\n");
3065 SetLastError(ERROR_INVALID_PARAMETER);
3066 return FALSE;
3067 }
3068
3069 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3070 {
3071 SetLastError(ERROR_INVALID_HANDLE);
3072 return FALSE;
3073 }
3074
3075 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3076 return FALSE;
3077 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3078 }
3079
3080 /***********************************************************************
3081 * RetrieveUrlCacheEntryStreamA (WININET.@)
3082 *
3083 */
3084 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3085 IN LPCSTR lpszUrlName,
3086 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3087 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3088 IN BOOL fRandomRead,
3089 IN DWORD dwReserved
3090 )
3091 {
3092 /* NOTE: this is not the same as the way that the native
3093 * version allocates 'stream' handles. I did it this way
3094 * as it is much easier and no applications should depend
3095 * on this behaviour. (Native version appears to allocate
3096 * indices into a table)
3097 */
3098 STREAM_HANDLE * pStream;
3099 HANDLE hFile;
3100
3101 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3102 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3103
3104 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3105 lpCacheEntryInfo,
3106 lpdwCacheEntryInfoBufferSize,
3107 dwReserved))
3108 {
3109 return NULL;
3110 }
3111
3112 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3113 GENERIC_READ,
3114 FILE_SHARE_READ,
3115 NULL,
3116 OPEN_EXISTING,
3117 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3118 NULL);
3119 if (hFile == INVALID_HANDLE_VALUE)
3120 return FALSE;
3121
3122 /* allocate handle storage space */
3123 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3124 if (!pStream)
3125 {
3126 CloseHandle(hFile);
3127 SetLastError(ERROR_OUTOFMEMORY);
3128 return FALSE;
3129 }
3130
3131 pStream->hFile = hFile;
3132 strcpy(pStream->lpszUrl, lpszUrlName);
3133 return pStream;
3134 }
3135
3136 /***********************************************************************
3137 * RetrieveUrlCacheEntryStreamW (WININET.@)
3138 *
3139 */
3140 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3141 IN LPCWSTR lpszUrlName,
3142 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3143 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3144 IN BOOL fRandomRead,
3145 IN DWORD dwReserved
3146 )
3147 {
3148 DWORD size;
3149 int url_len;
3150 /* NOTE: this is not the same as the way that the native
3151 * version allocates 'stream' handles. I did it this way
3152 * as it is much easier and no applications should depend
3153 * on this behaviour. (Native version appears to allocate
3154 * indices into a table)
3155 */
3156 STREAM_HANDLE * pStream;
3157 HANDLE hFile;
3158
3159 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3160 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3161
3162 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3163 lpCacheEntryInfo,
3164 lpdwCacheEntryInfoBufferSize,
3165 dwReserved))
3166 {
3167 return NULL;
3168 }
3169
3170 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3171 GENERIC_READ,
3172 FILE_SHARE_READ,
3173 NULL,
3174 OPEN_EXISTING,
3175 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3176 NULL);
3177 if (hFile == INVALID_HANDLE_VALUE)
3178 return FALSE;
3179
3180 /* allocate handle storage space */
3181 size = sizeof(STREAM_HANDLE);
3182 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3183 size += url_len;
3184 pStream = heap_alloc(size);
3185 if (!pStream)
3186 {
3187 CloseHandle(hFile);
3188 SetLastError(ERROR_OUTOFMEMORY);
3189 return FALSE;
3190 }
3191
3192 pStream->hFile = hFile;
3193 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3194 return pStream;
3195 }
3196
3197 /***********************************************************************
3198 * UnlockUrlCacheEntryStream (WININET.@)
3199 *
3200 */
3201 BOOL WINAPI UnlockUrlCacheEntryStream(
3202 IN HANDLE hUrlCacheStream,
3203 IN DWORD dwReserved
3204 )
3205 {
3206 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3207
3208 if (dwReserved != 0)
3209 {
3210 ERR("dwReserved != 0\n");
3211 SetLastError(ERROR_INVALID_PARAMETER);
3212 return FALSE;
3213 }
3214
3215 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3216 {
3217 SetLastError(ERROR_INVALID_HANDLE);
3218 return FALSE;
3219 }
3220
3221 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3222 return FALSE;
3223
3224 CloseHandle(pStream->hFile);
3225 heap_free(pStream);
3226 return TRUE;
3227 }
3228
3229
3230 /***********************************************************************
3231 * DeleteUrlCacheEntryA (WININET.@)
3232 *
3233 */
3234 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3235 {
3236 URLCACHECONTAINER * pContainer;
3237 LPURLCACHE_HEADER pHeader;
3238 struct _HASH_ENTRY * pHashEntry;
3239 DWORD error;
3240 BOOL ret;
3241
3242 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3243
3244 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3245 if (error != ERROR_SUCCESS)
3246 {
3247 SetLastError(error);
3248 return FALSE;
3249 }
3250
3251 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3252 if (error != ERROR_SUCCESS)
3253 {
3254 SetLastError(error);
3255 return FALSE;
3256 }
3257
3258 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3259 return FALSE;
3260
3261 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3262 {
3263 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3264 TRACE("entry %s not found!\n", lpszUrlName);
3265 SetLastError(ERROR_FILE_NOT_FOUND);
3266 return FALSE;
3267 }
3268
3269 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3270
3271 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3272
3273 return ret;
3274 }
3275
3276 /***********************************************************************
3277 * DeleteUrlCacheEntryW (WININET.@)
3278 *
3279 */
3280 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3281 {
3282 URLCACHECONTAINER * pContainer;
3283 LPURLCACHE_HEADER pHeader;
3284 struct _HASH_ENTRY * pHashEntry;
3285 LPSTR urlA;
3286 DWORD error;
3287 BOOL ret;
3288
3289 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3290
3291 urlA = heap_strdupWtoA(lpszUrlName);
3292 if (!urlA)
3293 {
3294 SetLastError(ERROR_OUTOFMEMORY);
3295 return FALSE;
3296 }
3297
3298 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3299 if (error != ERROR_SUCCESS)
3300 {
3301 heap_free(urlA);
3302 SetLastError(error);
3303 return FALSE;
3304 }
3305
3306 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3307 if (error != ERROR_SUCCESS)
3308 {
3309 heap_free(urlA);
3310 SetLastError(error);
3311 return FALSE;
3312 }
3313
3314 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3315 {
3316 heap_free(urlA);
3317 return FALSE;
3318 }
3319
3320 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3321 {
3322 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3323 TRACE("entry %s not found!\n", debugstr_a(urlA));
3324 heap_free(urlA);
3325 SetLastError(ERROR_FILE_NOT_FOUND);
3326 return FALSE;
3327 }
3328
3329 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3330
3331 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3332 heap_free(urlA);
3333 return ret;
3334 }
3335
3336 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3337 {
3338 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3339 return TRUE;
3340 }
3341
3342 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3343 {
3344 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3345 return TRUE;
3346 }
3347
3348 /***********************************************************************
3349 * CreateCacheContainerA (WININET.@)
3350 */
3351 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3352 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3353 {
3354 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3355 d1, d2, d3, d4, d5, d6, d7, d8);
3356 return TRUE;
3357 }
3358
3359 /***********************************************************************
3360 * CreateCacheContainerW (WININET.@)
3361 */
3362 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3363 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3364 {
3365 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3366 d1, d2, d3, d4, d5, d6, d7, d8);
3367 return TRUE;
3368 }
3369
3370 /***********************************************************************
3371 * FindFirstUrlCacheContainerA (WININET.@)
3372 */
3373 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3374 {
3375 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3376 return NULL;
3377 }
3378
3379 /***********************************************************************
3380 * FindFirstUrlCacheContainerW (WININET.@)
3381 */
3382 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3383 {
3384 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3385 return NULL;
3386 }
3387
3388 /***********************************************************************
3389 * FindNextUrlCacheContainerA (WININET.@)
3390 */
3391 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3392 {
3393 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3394 return FALSE;
3395 }
3396
3397 /***********************************************************************
3398 * FindNextUrlCacheContainerW (WININET.@)
3399 */
3400 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3401 {
3402 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3403 return FALSE;
3404 }
3405
3406 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3407 LPCSTR lpszUrlSearchPattern,
3408 DWORD dwFlags,
3409 DWORD dwFilter,
3410 GROUPID GroupId,
3411 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3412 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3413 LPVOID lpReserved,
3414 LPDWORD pcbReserved2,
3415 LPVOID lpReserved3
3416 )
3417 {
3418 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3419 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3420 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3421 SetLastError(ERROR_FILE_NOT_FOUND);
3422 return NULL;
3423 }
3424
3425 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3426 LPCWSTR lpszUrlSearchPattern,
3427 DWORD dwFlags,
3428 DWORD dwFilter,
3429 GROUPID GroupId,
3430 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3431 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3432 LPVOID lpReserved,
3433 LPDWORD pcbReserved2,
3434 LPVOID lpReserved3
3435 )
3436 {
3437 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3438 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3439 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3440 SetLastError(ERROR_FILE_NOT_FOUND);
3441 return NULL;
3442 }
3443
3444 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3445
3446 typedef struct URLCacheFindEntryHandle
3447 {
3448 DWORD dwMagic;
3449 LPWSTR lpszUrlSearchPattern;
3450 DWORD dwContainerIndex;
3451 DWORD dwHashTableIndex;
3452 DWORD dwHashEntryIndex;
3453 } URLCacheFindEntryHandle;
3454
3455 /***********************************************************************
3456 * FindFirstUrlCacheEntryA (WININET.@)
3457 *
3458 */
3459 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3460 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3461 {
3462 URLCacheFindEntryHandle *pEntryHandle;
3463
3464 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3465
3466 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3467 if (!pEntryHandle)
3468 return NULL;
3469
3470 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3471 if (lpszUrlSearchPattern)
3472 {
3473 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3474 if (!pEntryHandle->lpszUrlSearchPattern)
3475 {
3476 heap_free(pEntryHandle);
3477 return NULL;
3478 }
3479 }
3480 else
3481 pEntryHandle->lpszUrlSearchPattern = NULL;
3482 pEntryHandle->dwContainerIndex = 0;
3483 pEntryHandle->dwHashTableIndex = 0;
3484 pEntryHandle->dwHashEntryIndex = 0;
3485
3486 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3487 {
3488 heap_free(pEntryHandle);
3489 return NULL;
3490 }
3491 return pEntryHandle;
3492 }
3493
3494 /***********************************************************************
3495 * FindFirstUrlCacheEntryW (WININET.@)
3496 *
3497 */
3498 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3499 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3500 {
3501 URLCacheFindEntryHandle *pEntryHandle;
3502
3503 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3504
3505 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3506 if (!pEntryHandle)
3507 return NULL;
3508
3509 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3510 if (lpszUrlSearchPattern)
3511 {
3512 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3513 if (!pEntryHandle->lpszUrlSearchPattern)
3514 {
3515 heap_free(pEntryHandle);
3516 return NULL;
3517 }
3518 }
3519 else
3520 pEntryHandle->lpszUrlSearchPattern = NULL;
3521 pEntryHandle->dwContainerIndex = 0;
3522 pEntryHandle->dwHashTableIndex = 0;
3523 pEntryHandle->dwHashEntryIndex = 0;
3524
3525 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3526 {
3527 heap_free(pEntryHandle);
3528 return NULL;
3529 }
3530 return pEntryHandle;
3531 }
3532
3533 static BOOL FindNextUrlCacheEntryInternal(
3534 HANDLE hEnumHandle,
3535 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3536 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3537 BOOL unicode)
3538 {
3539 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3540 URLCACHECONTAINER * pContainer;
3541
3542 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3543 {
3544 SetLastError(ERROR_INVALID_HANDLE);
3545 return FALSE;
3546 }
3547
3548 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3549 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3550 {
3551 LPURLCACHE_HEADER pHeader;
3552 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3553 DWORD error;
3554
3555 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3556 if (error != ERROR_SUCCESS)
3557 {
3558 SetLastError(error);
3559 return FALSE;
3560 }
3561
3562 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3563 return FALSE;
3564
3565 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3566 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3567 {
3568 const struct _HASH_ENTRY *pHashEntry = NULL;
3569 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3570 pEntryHandle->dwHashEntryIndex++)
3571 {
3572 const URL_CACHEFILE_ENTRY *pUrlEntry;
3573 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3574
3575 if (pEntry->dwSignature != URL_SIGNATURE)
3576 continue;
3577
3578 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3579 TRACE("Found URL: %s\n",
3580 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3581 TRACE("Header info: %s\n",
3582 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3583
3584 error = URLCache_CopyEntry(
3585 pContainer,
3586 pHeader,
3587 lpNextCacheEntryInfo,
3588 lpdwNextCacheEntryInfoBufferSize,
3589 pUrlEntry,
3590 unicode);
3591 if (error != ERROR_SUCCESS)
3592 {
3593 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3594 SetLastError(error);
3595 return FALSE;
3596 }
3597 TRACE("Local File Name: %s\n",
3598 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3599
3600 /* increment the current index so that next time the function
3601 * is called the next entry is returned */
3602 pEntryHandle->dwHashEntryIndex++;
3603 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3604 return TRUE;
3605 }
3606 }
3607
3608 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3609 }
3610
3611 SetLastError(ERROR_NO_MORE_ITEMS);
3612 return FALSE;
3613 }
3614
3615 /***********************************************************************
3616 * FindNextUrlCacheEntryA (WININET.@)
3617 */
3618 BOOL WINAPI FindNextUrlCacheEntryA(
3619 HANDLE hEnumHandle,
3620 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3621 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3622 {
3623 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3624
3625 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3626 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3627 }
3628
3629 /***********************************************************************
3630 * FindNextUrlCacheEntryW (WININET.@)
3631 */
3632 BOOL WINAPI FindNextUrlCacheEntryW(
3633 HANDLE hEnumHandle,
3634 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3635 LPDWORD lpdwNextCacheEntryInfoBufferSize
3636 )
3637 {
3638 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3639
3640 return FindNextUrlCacheEntryInternal(hEnumHandle,
3641 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3642 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3643 }
3644
3645 /***********************************************************************
3646 * FindCloseUrlCache (WININET.@)
3647 */
3648 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3649 {
3650 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3651
3652 TRACE("(%p)\n", hEnumHandle);
3653
3654 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3655 {
3656 SetLastError(ERROR_INVALID_HANDLE);
3657 return FALSE;
3658 }
3659
3660 pEntryHandle->dwMagic = 0;
3661 heap_free(pEntryHandle->lpszUrlSearchPattern);
3662 heap_free(pEntryHandle);
3663 return TRUE;
3664 }
3665
3666 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3667 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3668 {
3669 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3670 dwSearchCondition, lpGroupId, lpReserved);
3671 return NULL;
3672 }
3673
3674 BOOL WINAPI FindNextUrlCacheEntryExA(
3675 HANDLE hEnumHandle,
3676 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3677 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3678 LPVOID lpReserved,
3679 LPDWORD pcbReserved2,
3680 LPVOID lpReserved3
3681 )
3682 {
3683 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3684 lpReserved, pcbReserved2, lpReserved3);
3685 return FALSE;
3686 }
3687
3688 BOOL WINAPI FindNextUrlCacheEntryExW(
3689 HANDLE hEnumHandle,
3690 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3691 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3692 LPVOID lpReserved,
3693 LPDWORD pcbReserved2,
3694 LPVOID lpReserved3
3695 )
3696 {
3697 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3698 lpReserved, pcbReserved2, lpReserved3);
3699 return FALSE;
3700 }
3701
3702 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3703 {
3704 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3705 return FALSE;
3706 }
3707
3708 /***********************************************************************
3709 * CreateUrlCacheGroup (WININET.@)
3710 *
3711 */
3712 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3713 {
3714 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3715 return FALSE;
3716 }
3717
3718 /***********************************************************************
3719 * DeleteUrlCacheGroup (WININET.@)
3720 *
3721 */
3722 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3723 {
3724 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3725 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3726 return FALSE;
3727 }
3728
3729 /***********************************************************************
3730 * SetUrlCacheEntryGroupA (WININET.@)
3731 *
3732 */
3733 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3734 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3735 LPVOID lpReserved)
3736 {
3737 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3738 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3739 pbGroupAttributes, cbGroupAttributes, lpReserved);
3740 SetLastError(ERROR_FILE_NOT_FOUND);
3741 return FALSE;
3742 }
3743
3744 /***********************************************************************
3745 * SetUrlCacheEntryGroupW (WININET.@)
3746 *
3747 */
3748 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3749 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3750 LPVOID lpReserved)
3751 {
3752 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3753 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3754 pbGroupAttributes, cbGroupAttributes, lpReserved);
3755 SetLastError(ERROR_FILE_NOT_FOUND);
3756 return FALSE;
3757 }
3758
3759 /***********************************************************************
3760 * GetUrlCacheConfigInfoW (WININET.@)
3761 */
3762 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3763 {
3764 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3765 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3766 return FALSE;
3767 }
3768
3769 /***********************************************************************
3770 * GetUrlCacheConfigInfoA (WININET.@)
3771 */
3772 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3773 {
3774 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3775 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3776 return FALSE;
3777 }
3778
3779 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3780 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3781 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3782 {
3783 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3784 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3785 lpdwGroupInfo, lpReserved);
3786 return FALSE;
3787 }
3788
3789 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3790 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3791 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3792 {
3793 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3794 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3795 lpdwGroupInfo, lpReserved);
3796 return FALSE;
3797 }
3798
3799 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3800 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3801 {
3802 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3803 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3804 return TRUE;
3805 }
3806
3807 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3808 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3809 {
3810 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3811 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3812 return TRUE;
3813 }
3814
3815 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3816 {
3817 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3818 return TRUE;
3819 }
3820
3821 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3822 {
3823 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3824 return TRUE;
3825 }
3826
3827 /***********************************************************************
3828 * DeleteIE3Cache (WININET.@)
3829 *
3830 * Deletes the files used by the IE3 URL caching system.
3831 *
3832 * PARAMS
3833 * hWnd [I] A dummy window.
3834 * hInst [I] Instance of process calling the function.
3835 * lpszCmdLine [I] Options used by function.
3836 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3837 */
3838 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3839 {
3840 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3841 return 0;
3842 }
3843
3844 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3845 FILETIME *pftLastModified)
3846 {
3847 BOOL ret;
3848 FILETIME now, expired;
3849
3850 *pftLastModified = pUrlEntry->LastModifiedTime;
3851 GetSystemTimeAsFileTime(&now);
3852 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3853 pUrlEntry->wExpiredTime, &expired);
3854 /* If the expired time is 0, it's interpreted as not expired */
3855 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3856 ret = FALSE;
3857 else
3858 ret = CompareFileTime(&expired, &now) < 0;
3859 return ret;
3860 }
3861
3862 /***********************************************************************
3863 * IsUrlCacheEntryExpiredA (WININET.@)
3864 *
3865 * PARAMS
3866 * url [I] Url
3867 * dwFlags [I] Unknown
3868 * pftLastModified [O] Last modified time
3869 */
3870 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3871 {
3872 LPURLCACHE_HEADER pHeader;
3873 struct _HASH_ENTRY * pHashEntry;
3874 const CACHEFILE_ENTRY * pEntry;
3875 const URL_CACHEFILE_ENTRY * pUrlEntry;
3876 URLCACHECONTAINER * pContainer;
3877 BOOL expired;
3878
3879 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3880
3881 if (!url || !pftLastModified)
3882 return TRUE;
3883 if (dwFlags)
3884 FIXME("unknown flags 0x%08x\n", dwFlags);
3885
3886 /* Any error implies that the URL is expired, i.e. not in the cache */
3887 if (URLCacheContainers_FindContainerA(url, &pContainer))
3888 {
3889 memset(pftLastModified, 0, sizeof(*pftLastModified));
3890 return TRUE;
3891 }
3892
3893 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3894 {
3895 memset(pftLastModified, 0, sizeof(*pftLastModified));
3896 return TRUE;
3897 }
3898
3899 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3900 {
3901 memset(pftLastModified, 0, sizeof(*pftLastModified));
3902 return TRUE;
3903 }
3904
3905 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3906 {
3907 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3908 memset(pftLastModified, 0, sizeof(*pftLastModified));
3909 TRACE("entry %s not found!\n", url);
3910 return TRUE;
3911 }
3912
3913 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3914 if (pEntry->dwSignature != URL_SIGNATURE)
3915 {
3916 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3917 memset(pftLastModified, 0, sizeof(*pftLastModified));
3918 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3919 return TRUE;
3920 }
3921
3922 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3923 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3924
3925 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3926
3927 return expired;
3928 }
3929
3930 /***********************************************************************
3931 * IsUrlCacheEntryExpiredW (WININET.@)
3932 *
3933 * PARAMS
3934 * url [I] Url
3935 * dwFlags [I] Unknown
3936 * pftLastModified [O] Last modified time
3937 */
3938 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3939 {
3940 LPURLCACHE_HEADER pHeader;
3941 struct _HASH_ENTRY * pHashEntry;
3942 const CACHEFILE_ENTRY * pEntry;
3943 const URL_CACHEFILE_ENTRY * pUrlEntry;
3944 URLCACHECONTAINER * pContainer;
3945 BOOL expired;
3946
3947 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3948
3949 if (!url || !pftLastModified)
3950 return TRUE;
3951 if (dwFlags)
3952 FIXME("unknown flags 0x%08x\n", dwFlags);
3953
3954 /* Any error implies that the URL is expired, i.e. not in the cache */
3955 if (URLCacheContainers_FindContainerW(url, &pContainer))
3956 {
3957 memset(pftLastModified, 0, sizeof(*pftLastModified));
3958 return TRUE;
3959 }
3960
3961 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3962 {
3963 memset(pftLastModified, 0, sizeof(*pftLastModified));
3964 return TRUE;
3965 }
3966
3967 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3968 {
3969 memset(pftLastModified, 0, sizeof(*pftLastModified));
3970 return TRUE;
3971 }
3972
3973 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3974 {
3975 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3976 memset(pftLastModified, 0, sizeof(*pftLastModified));
3977 TRACE("entry %s not found!\n", debugstr_w(url));
3978 return TRUE;
3979 }
3980
3981 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3982 {
3983 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3984 memset(pftLastModified, 0, sizeof(*pftLastModified));
3985 TRACE("entry %s not found!\n", debugstr_w(url));
3986 return TRUE;
3987 }
3988
3989 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3990 if (pEntry->dwSignature != URL_SIGNATURE)
3991 {
3992 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3993 memset(pftLastModified, 0, sizeof(*pftLastModified));
3994 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3995 return TRUE;
3996 }
3997
3998 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3999 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4000
4001 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4002
4003 return expired;
4004 }
4005
4006 /***********************************************************************
4007 * GetDiskInfoA (WININET.@)
4008 */
4009 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4010 {
4011 BOOL ret;
4012 ULARGE_INTEGER bytes_free, bytes_total;
4013
4014 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4015
4016 if (!path)
4017 {
4018 SetLastError(ERROR_INVALID_PARAMETER);
4019 return FALSE;
4020 }
4021
4022 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4023 {
4024 if (cluster_size) *cluster_size = 1;
4025 if (free) *free = bytes_free.QuadPart;
4026 if (total) *total = bytes_total.QuadPart;
4027 }
4028 return ret;
4029 }
4030
4031 /***********************************************************************
4032 * RegisterUrlCacheNotification (WININET.@)
4033 */
4034 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4035 {
4036 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4037 return 0;
4038 }
4039
4040 /***********************************************************************
4041 * IncrementUrlCacheHeaderData (WININET.@)
4042 */
4043 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4044 {
4045 FIXME("(%u, %p)\n", index, data);
4046 return FALSE;
4047 }
4048
4049 /***********************************************************************
4050 * RunOnceUrlCache (WININET.@)
4051 */
4052
4053 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4054 {
4055 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4056 return 0;
4057 }