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