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