ReactOS 0.4.15-dev-8102-g108db8f
urlcache.c
Go to the documentation of this file.
1#ifdef __REACTOS__
2#define NONAMELESSUNION
3#define NONAMELESSSTRUCT
4#include "precomp.h"
5#else
6/*
7 * Wininet - Url Cache functions
8 *
9 * Copyright 2001,2002 CodeWeavers
10 * Copyright 2003-2008 Robert Shearman
11 *
12 * Eric Kohl
13 * Aric Stewart
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 */
29
30#define NONAMELESSUNION
31#define NONAMELESSSTRUCT
32
33#include "ws2tcpip.h"
34
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <time.h>
40
41#include "windef.h"
42#include "winbase.h"
43#include "winuser.h"
44#include "wininet.h"
45#include "winineti.h"
46#include "winerror.h"
47#include "winreg.h"
48#include "shlwapi.h"
49#include "shlobj.h"
50#include "shellapi.h"
51
52#include "internet.h"
53#include "wine/debug.h"
54#endif /* defined(__REACTOS__) */
55
57
58static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
59static const char urlcache_ver[] = "0.2012001";
60
61#ifndef CHAR_BIT
62#define CHAR_BIT (8 * sizeof(CHAR))
63#endif
64
65#define ENTRY_START_OFFSET 0x4000
66#define DIR_LENGTH 8
67#define MAX_DIR_NO 0x20
68#define BLOCKSIZE 128
69#define HASHTABLE_SIZE 448
70#define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
71#define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
72#define ALLOCATION_TABLE_OFFSET 0x250
73#define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
74#define MIN_BLOCK_NO 0x80
75#define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
76#define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
77
78#define HASHTABLE_URL 0
79#define HASHTABLE_DEL 1
80#define HASHTABLE_LOCK 2
81#define HASHTABLE_FREE 3
82#define HASHTABLE_REDR 5
83#define HASHTABLE_FLAG_BITS 6
84
85#define PENDING_DELETE_CACHE_ENTRY 0x00400000
86#define INSTALLED_CACHE_ENTRY 0x10000000
87#define GET_INSTALLED_ENTRY 0x200
88#define CACHE_CONTAINER_NO_SUBDIR 0xFE
89
90#define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
91
92#define FILETIME_SECOND 10000000
93
94#define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
95#define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
96#define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
97#define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
98#define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
99
100#define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
101
102#define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
103
104typedef struct
105{
107 DWORD blocks_used; /* number of 128byte blocks used by this entry */
109
110typedef struct
111{
115 WORD expire_date; /* expire date in dos format */
116 WORD expire_time; /* expire time in dos format */
117 DWORD unk1; /* usually zero */
118 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
119 DWORD unk2; /* usually zero */
120 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
121 DWORD unk3; /* usually 0x60 */
122 DWORD url_off; /* offset of start of url from start of entry */
123 BYTE cache_dir; /* index of cache directory this url is stored in */
124 BYTE unk4; /* usually zero */
125 WORD unk5; /* usually 0x1010 */
126 DWORD local_name_off; /* offset of start of local filename from start of entry */
127 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
128 DWORD header_info_off; /* offset of start of header info from start of entry */
130 DWORD file_extension_off; /* offset of start of file extension from start of entry */
131 WORD sync_date; /* last sync date in dos format */
132 WORD sync_time; /* last sync time in dos format */
133 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
134 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
137 DWORD unk7; /* usually zero */
138 DWORD unk8; /* usually zero */
139 /* packing to dword align start of next field */
140 /* CHAR szSourceUrlName[]; (url) */
141 /* packing to dword align start of next field */
142 /* CHAR szLocalFileName[]; (local file name excluding path) */
143 /* packing to dword align start of next field */
144 /* CHAR szHeaderInfo[]; (header info) */
145} entry_url;
146
148{
151};
152
153typedef struct
154{
160
161typedef struct
162{
163 char signature[28];
174 {
177 } directory_data[MAX_DIR_NO];
179 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
181
182typedef struct
183{
187
188typedef struct
189{
190 struct list entry; /* part of a list */
191 char *cache_prefix; /* string that has to be prefixed for this container to be used */
192 LPWSTR path; /* path to url container directory */
193 HANDLE mapping; /* handle of file mapping */
194 DWORD file_size; /* size of file when mapping was opened */
195 HANDLE mutex; /* handle of mutex */
198
199typedef struct
200{
207
208/* List of all containers available */
210/* ReactOS r54992 */
212
213static inline char *heap_strdupWtoUTF8(LPCWSTR str)
214{
215 char *ret = NULL;
216
217 if(str) {
220 if(ret)
222 }
223
224 return ret;
225}
226
227/***********************************************************************
228 * urlcache_block_is_free (Internal)
229 *
230 * Is the specified block number free?
231 *
232 * RETURNS
233 * zero if free
234 * non-zero otherwise
235 *
236 */
237static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
238{
239 BYTE mask = 1 << (block_number%CHAR_BIT);
240 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
241}
242
243/***********************************************************************
244 * urlcache_block_free (Internal)
245 *
246 * Marks the specified block as free
247 *
248 * CAUTION
249 * this function is not updating used blocks count
250 *
251 * RETURNS
252 * nothing
253 *
254 */
255static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
256{
257 BYTE mask = ~(1 << (block_number%CHAR_BIT));
258 allocation_table[block_number/CHAR_BIT] &= mask;
259}
260
261/***********************************************************************
262 * urlcache_block_alloc (Internal)
263 *
264 * Marks the specified block as allocated
265 *
266 * CAUTION
267 * this function is not updating used blocks count
268 *
269 * RETURNS
270 * nothing
271 *
272 */
273static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
274{
275 BYTE mask = 1 << (block_number%CHAR_BIT);
276 allocation_table[block_number/CHAR_BIT] |= mask;
277}
278
279/***********************************************************************
280 * urlcache_entry_alloc (Internal)
281 *
282 * Finds and allocates the first block of free space big enough and
283 * sets entry to point to it.
284 *
285 * RETURNS
286 * ERROR_SUCCESS when free memory block was found
287 * Any other Win32 error code if the entry could not be added
288 *
289 */
291{
293
294 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
295 {
296 block_size = 0;
297 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
298 && urlcache_block_is_free(header->allocation_table, block+block_size))
299 block_size++;
300
301 if(block_size == blocks_needed)
302 {
303 DWORD index;
304
305 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
306
307 for(index=0; index<blocks_needed; index++)
308 urlcache_block_alloc(header->allocation_table, block+index);
309
311 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
312 ((DWORD*)*entry)[index] = 0xdeadbeef;
313 (*entry)->blocks_used = blocks_needed;
314
315 header->blocks_in_use += blocks_needed;
316 return ERROR_SUCCESS;
317 }
318 }
319
321}
322
323/***********************************************************************
324 * urlcache_entry_free (Internal)
325 *
326 * Deletes the specified entry and frees the space allocated to it
327 *
328 * RETURNS
329 * TRUE if it succeeded
330 * FALSE if it failed
331 *
332 */
334{
335 DWORD start_block, block;
336
337 /* update allocation table */
338 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
339 for(block = start_block; block < start_block+entry->blocks_used; block++)
340 urlcache_block_free(header->allocation_table, block);
341
342 header->blocks_in_use -= entry->blocks_used;
343 return TRUE;
344}
345
346/***********************************************************************
347 * urlcache_create_hash_table (Internal)
348 *
349 * Creates a new hash table in free space and adds it to the chain of existing
350 * hash tables.
351 *
352 * RETURNS
353 * ERROR_SUCCESS if the hash table was created
354 * ERROR_DISK_FULL if the hash table could not be created
355 *
356 */
358{
360 int i;
361
363 return error;
364
366
367 if(hash_table_prev)
368 hash_table_prev->next = dwOffset;
369 else
370 header->hash_table_off = dwOffset;
371
372 (*hash_table)->header.signature = HASH_SIGNATURE;
373 (*hash_table)->next = 0;
374 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
375 for(i = 0; i < HASHTABLE_SIZE; i++) {
376 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
377 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
378 }
379 return ERROR_SUCCESS;
380}
381
382/***********************************************************************
383 * cache_container_create_object_name (Internal)
384 *
385 * Converts a path to a name suitable for use as a Win32 object name.
386 * Replaces '\\' characters in-place with the specified character
387 * (usually '_' or '!')
388 *
389 * RETURNS
390 * nothing
391 *
392 */
394{
395 for (; *lpszPath; lpszPath++)
396 {
397 if (*lpszPath == '\\')
398 *lpszPath = replace;
399 }
400}
401
402/* Caller must hold container lock */
404{
405 WCHAR mapping_name[MAX_PATH];
407
408 wsprintfW(mapping_name, L"%sindex.dat_%lu", path, size);
409 cache_container_create_object_name(mapping_name, '_');
410
412 if(mapping) {
413 if(validate) *validate = FALSE;
414 return mapping;
415 }
416
417 if(validate) *validate = TRUE;
418 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
419}
420
421/* Caller must hold container lock */
423{
424 DWORD file_size = FILE_SIZE(blocks_no);
425 WCHAR dir_path[MAX_PATH], *dir_name;
426 entry_hash_table *hashtable_entry;
429 FILETIME ft;
430 HKEY key;
431 int i, j;
432
434 return GetLastError();
435
436 if(!SetEndOfFile(file))
437 return GetLastError();
438
440 if(!mapping)
441 return GetLastError();
442
444 if(!header) {
446 return GetLastError();
447 }
448
449 if(blocks_no != MIN_BLOCK_NO) {
450 if(file_size > header->size)
451 memset((char*)header+header->size, 0, file_size-header->size);
452 header->size = file_size;
453 header->capacity_in_blocks = blocks_no;
454
456 CloseHandle(container->mapping);
457 container->mapping = mapping;
458 container->file_size = file_size;
459 return ERROR_SUCCESS;
460 }
461
463 /* First set some constants and defaults in the header */
465 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
466 header->size = file_size;
467 header->capacity_in_blocks = blocks_no;
468 /* 127MB - taken from default for Windows 2000 */
469 header->cache_limit.QuadPart = 0x07ff5400;
470 /* Copied from a Windows 2000 cache index */
471 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
472
473 /* If the registry has a cache size set, use the registry value */
475 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content", &key) == ERROR_SUCCESS) {
476 DWORD dw, len = sizeof(dw), keytype;
477
478 if(RegQueryValueExW(key, L"CacheLimit", NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
479 keytype == REG_DWORD)
480 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
482 }
483
484 urlcache_create_hash_table(header, NULL, &hashtable_entry);
485
486 /* Last step - create the directories */
487 lstrcpyW(dir_path, container->path);
488 dir_name = dir_path + lstrlenW(dir_path);
489 dir_name[8] = 0;
490
492
493 for(i=0; i<header->dirs_no; ++i) {
494 header->directory_data[i].files_no = 0;
495 for(j=0;; ++j) {
497 int k;
498
499 /* Generate a file name to attempt to create.
500 * This algorithm will create what will appear
501 * to be random and unrelated directory names
502 * of up to 9 characters in length.
503 */
504 n <<= 32;
505 n += ft.dwLowDateTime;
506 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
507
508 for(k = 0; k < 8; ++k) {
509 int r = (n % 36);
510
511 /* Dividing by a prime greater than 36 helps
512 * with the appearance of randomness
513 */
514 n /= 37;
515
516 if(r < 10)
517 dir_name[k] = '0' + r;
518 else
519 dir_name[k] = 'A' + (r - 10);
520 }
521
522 if(CreateDirectoryW(dir_path, 0)) {
523 /* The following is OK because we generated an
524 * 8 character directory name made from characters
525 * [A-Z0-9], which are equivalent for all code
526 * pages and for UTF-16
527 */
528 for (k = 0; k < 8; ++k)
529 header->directory_data[i].name[k] = dir_name[k];
530 break;
531 }else if(j >= 255) {
532 /* Give up. The most likely cause of this
533 * is a full disk, but whatever the cause
534 * is, it should be more than apparent that
535 * we won't succeed.
536 */
539 return GetLastError();
540 }
541 }
542 }
543
545 CloseHandle(container->mapping);
546 container->mapping = mapping;
547 container->file_size = file_size;
548 return ERROR_SUCCESS;
549}
550
552{
553 DWORD allocation_size, count_bits, i;
554
556 return FALSE;
557
558 if(file_size != header->size)
559 return FALSE;
560
561 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
562 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
563 return FALSE;
564
565 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
566 return FALSE;
567
568 allocation_size = 0;
569 for(i=0; i<header->capacity_in_blocks/8; i++) {
570 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
571 if(count_bits & 1)
572 allocation_size++;
573 }
574 }
575 if(allocation_size != header->blocks_in_use)
576 return FALSE;
577
578 for(; i<ALLOCATION_TABLE_SIZE; i++) {
579 if(header->allocation_table[i])
580 return FALSE;
581 }
582
583 return TRUE;
584}
585
586/***********************************************************************
587 * cache_container_open_index (Internal)
588 *
589 * Opens the index file and saves mapping handle
590 *
591 * RETURNS
592 * ERROR_SUCCESS if succeeded
593 * Any other Win32 error code if failed
594 *
595 */
597{
598 HANDLE file;
599 WCHAR index_path[MAX_PATH];
602
604
605 if(container->mapping) {
606 ReleaseMutex(container->mutex);
607 return ERROR_SUCCESS;
608 }
609
610 lstrcpyW(index_path, container->path);
611 lstrcatW(index_path, L"index.dat");
612
615 /* Maybe the directory wasn't there? Try to create it */
616 if(CreateDirectoryW(container->path, 0))
618 }
620 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
621 ReleaseMutex(container->mutex);
622 return GetLastError();
623 }
624
628 ReleaseMutex(container->mutex);
629 return GetLastError();
630 }
631
632 if(blocks_no < MIN_BLOCK_NO)
633 blocks_no = MIN_BLOCK_NO;
634 else if(blocks_no > MAX_BLOCK_NO)
635 blocks_no = MAX_BLOCK_NO;
636
637 if(file_size < FILE_SIZE(blocks_no)) {
640 ReleaseMutex(container->mutex);
641 return ret;
642 }
643
644 container->file_size = file_size;
647 if(container->mapping && validate) {
649
651 WARN("detected old or broken index.dat file\n");
653 FreeUrlCacheSpaceW(container->path, 100, 0);
654 }else if(header) {
656 }else {
657 CloseHandle(container->mapping);
658 container->mapping = NULL;
659 }
660 }
661
662 if(!container->mapping)
663 {
664 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
665 ReleaseMutex(container->mutex);
666 return GetLastError();
667 }
668
669 ReleaseMutex(container->mutex);
670 return ERROR_SUCCESS;
671}
672
673/***********************************************************************
674 * cache_container_close_index (Internal)
675 *
676 * Closes the index
677 *
678 * RETURNS
679 * nothing
680 *
681 */
683{
684 CloseHandle(pContainer->mapping);
685 pContainer->mapping = NULL;
686}
687
688static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
689 DWORD default_entry_type, LPWSTR mutex_name)
690{
691 cache_container *pContainer = heap_alloc(sizeof(cache_container));
692 int cache_prefix_len = strlen(cache_prefix);
693
694 if (!pContainer)
695 {
696 return FALSE;
697 }
698
699 pContainer->mapping = NULL;
700 pContainer->file_size = 0;
701 pContainer->default_entry_type = default_entry_type;
702
703 pContainer->path = heap_strdupW(path);
704 if (!pContainer->path)
705 {
706 heap_free(pContainer);
707 return FALSE;
708 }
709
710 pContainer->cache_prefix = heap_alloc(cache_prefix_len+1);
711 if (!pContainer->cache_prefix)
712 {
713 heap_free(pContainer->path);
714 heap_free(pContainer);
715 return FALSE;
716 }
717
718 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
719
720 CharLowerW(mutex_name);
721 cache_container_create_object_name(mutex_name, '!');
722
723 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
724 {
725 ERR("couldn't create mutex (error is %d)\n", GetLastError());
726 heap_free(pContainer->path);
727 heap_free(pContainer);
728 return FALSE;
729 }
730
731 list_add_head(&UrlContainers, &pContainer->entry);
732
733 return TRUE;
734}
735
737{
738 list_remove(&pContainer->entry);
739
740 cache_container_close_index(pContainer);
741 CloseHandle(pContainer->mutex);
742 heap_free(pContainer->path);
743 heap_free(pContainer->cache_prefix);
744 heap_free(pContainer);
745}
746
747static void cache_containers_init(void)
748{
749 /* ReactOS r50916 */
750 static const struct
751 {
752 int nFolder; /* CSIDL_* constant */
753 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
754 const char *cache_prefix; /* prefix used to reference the container */
755 DWORD default_entry_type;
756 } DefaultContainerData[] =
757 {
758 { CSIDL_INTERNET_CACHE, L"Content.IE5", "", NORMAL_CACHE_ENTRY },
759 { CSIDL_HISTORY, L"History.IE5", "Visited:", URLHISTORY_CACHE_ENTRY },
760 { CSIDL_COOKIES, L"", "Cookie:", COOKIE_CACHE_ENTRY },
761 };
762 DWORD i;
763
764 /* ReactOS r50916 */
765 if (GetEnvironmentVariableW(L"USERPROFILE", NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND)
766 {
767 ERR("Environment variable 'USERPROFILE' does not exist!\n");
768 return;
769 }
770
771 for (i = 0; i < ARRAY_SIZE(DefaultContainerData); i++)
772 {
773 WCHAR wszCachePath[MAX_PATH];
774 WCHAR wszMutexName[MAX_PATH];
775 int path_len, suffix_len;
776 BOOL def_char;
777
778 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
779 {
780 ERR("Couldn't get path for default container %u\n", i);
781 continue;
782 }
783 path_len = lstrlenW(wszCachePath);
784 suffix_len = lstrlenW(DefaultContainerData[i].shpath_suffix);
785
786 if (path_len + suffix_len + 2 > MAX_PATH)
787 {
788 ERR("Path too long\n");
789 continue;
790 }
791
792 wszCachePath[path_len] = '\\';
793 wszCachePath[path_len+1] = 0;
794
795 lstrcpyW(wszMutexName, wszCachePath);
796
797 if (suffix_len)
798 {
799 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
800 wszCachePath[path_len + suffix_len + 1] = '\\';
801 wszCachePath[path_len + suffix_len + 2] = '\0';
802 }
803
805 NULL, 0, NULL, &def_char) || def_char)
806 {
807 WCHAR tmp[MAX_PATH];
808
809 /* cannot convert path to ANSI code page */
810 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
812 NULL, 0, NULL, &def_char) || def_char)
813 ERR("Can't create container path accessible by ANSI functions\n");
814 else
815 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
816 }
817
818 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
819 DefaultContainerData[i].default_entry_type, wszMutexName);
820 }
821
822#ifdef __REACTOS__
824#endif
825}
826
827static void cache_containers_free(void)
828{
829 while(!list_empty(&UrlContainers))
832 );
833}
834
836{
838
839 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
840
841 if(!url)
843
844#ifdef __REACTOS__
845 /* ReactOS r54992 */
848#endif
849
851 {
852 int prefix_len = strlen(container->cache_prefix);
853
854 if(!strncmp(container->cache_prefix, url, prefix_len)) {
855 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
856 *ret = container;
857 return ERROR_SUCCESS;
858 }
859 }
860
861 ERR("no container found\n");
863}
864
865static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
866{
867 DWORD i = 0;
869
870 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
871
872 /* non-NULL search pattern only returns one container ever */
873 if (search_pattern && index > 0)
874 return FALSE;
875
876#ifdef __REACTOS__
877 /* ReactOS r54992 */
880#endif
881
883 {
884 if (search_pattern)
885 {
886 if (!strcmp(container->cache_prefix, search_pattern))
887 {
888 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
889 *ret = container;
890 return TRUE;
891 }
892 }
893 else
894 {
895 if (i == index)
896 {
897 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
898 *ret = container;
899 return TRUE;
900 }
901 }
902 i++;
903 }
904 return FALSE;
905}
906
907/***********************************************************************
908 * cache_container_lock_index (Internal)
909 *
910 * Locks the index for system-wide exclusive access.
911 *
912 * RETURNS
913 * Cache file header if successful
914 * NULL if failed and calls SetLastError.
915 */
917{
918 BYTE index;
919 LPVOID pIndexData;
921 DWORD error;
922
923 /* acquire mutex */
924 WaitForSingleObject(pContainer->mutex, INFINITE);
925
926 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
927
928 if (!pIndexData)
929 {
930 ReleaseMutex(pContainer->mutex);
931 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
932 return NULL;
933 }
934 pHeader = (urlcache_header*)pIndexData;
935
936 /* file has grown - we need to remap to prevent us getting
937 * access violations when we try and access beyond the end
938 * of the memory mapped file */
939 if (pHeader->size != pContainer->file_size)
940 {
942 cache_container_close_index(pContainer);
944 if (error != ERROR_SUCCESS)
945 {
946 ReleaseMutex(pContainer->mutex);
948 return NULL;
949 }
950 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
951
952 if (!pIndexData)
953 {
954 ReleaseMutex(pContainer->mutex);
955 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
956 return NULL;
957 }
958 pHeader = (urlcache_header*)pIndexData;
959 }
960
961 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
962
963 for (index = 0; index < pHeader->dirs_no; index++)
964 {
965 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
966 }
967
968 return pHeader;
969}
970
971/***********************************************************************
972 * cache_container_unlock_index (Internal)
973 *
974 */
976{
977 /* release mutex */
978 ReleaseMutex(pContainer->mutex);
979 return UnmapViewOfFile(pHeader);
980}
981
982/***********************************************************************
983 * urlcache_create_file_pathW (Internal)
984 *
985 * Copies the full path to the specified buffer given the local file
986 * name and the index of the directory it is in. Always sets value in
987 * lpBufferSize to the required buffer size (in bytes).
988 *
989 * RETURNS
990 * TRUE if the buffer was big enough
991 * FALSE if the buffer was too small
992 *
993 */
995 const cache_container *pContainer,
997 LPCSTR szLocalFileName,
999 LPWSTR wszPath,
1000 LPLONG lpBufferSize,
1001 BOOL trunc_name)
1002{
1003 LONG nRequired;
1004 int path_len = lstrlenW(pContainer->path);
1005 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
1007 {
1008 *lpBufferSize = 0;
1009 return FALSE;
1010 }
1011
1012 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
1014 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
1015 if (trunc_name && nRequired >= *lpBufferSize)
1016 nRequired = *lpBufferSize;
1017 if (nRequired <= *lpBufferSize)
1018 {
1019 int dir_len;
1020
1021 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1023 {
1024 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1025 wszPath[dir_len + path_len] = '\\';
1026 dir_len++;
1027 }
1028 else
1029 {
1030 dir_len = 0;
1031 }
1032 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len,
1033 *lpBufferSize/sizeof(WCHAR)-dir_len-path_len);
1034 wszPath[*lpBufferSize/sizeof(WCHAR)-1] = 0;
1035 *lpBufferSize = nRequired;
1036 return TRUE;
1037 }
1038 *lpBufferSize = nRequired;
1039 return FALSE;
1040}
1041
1042/***********************************************************************
1043 * urlcache_create_file_pathA (Internal)
1044 *
1045 * Copies the full path to the specified buffer given the local file
1046 * name and the index of the directory it is in. Always sets value in
1047 * lpBufferSize to the required buffer size.
1048 *
1049 * RETURNS
1050 * TRUE if the buffer was big enough
1051 * FALSE if the buffer was too small
1052 *
1053 */
1055 const cache_container *pContainer,
1056 const urlcache_header *pHeader,
1057 LPCSTR szLocalFileName,
1059 LPSTR szPath,
1060 LPLONG lpBufferSize)
1061{
1062 LONG nRequired;
1063 int path_len, file_name_len, dir_len;
1064
1066 {
1067 *lpBufferSize = 0;
1068 return FALSE;
1069 }
1070
1071 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1072 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1074 dir_len = DIR_LENGTH+1;
1075 else
1076 dir_len = 0;
1077
1078 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1079 if (nRequired <= *lpBufferSize)
1080 {
1081 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1082 if(dir_len) {
1083 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1084 szPath[path_len + dir_len-1] = '\\';
1085 }
1086 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1087 *lpBufferSize = nRequired;
1088 return TRUE;
1089 }
1090 *lpBufferSize = nRequired;
1091 return FALSE;
1092}
1093
1094/* Just like FileTimeToDosDateTime, except that it also maps the special
1095 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1096 */
1097static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1098 WORD *fattime)
1099{
1100 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1101 *fatdate = *fattime = 0;
1102 else
1103 FileTimeToDosDateTime(ft, fatdate, fattime);
1104}
1105
1106/***********************************************************************
1107 * urlcache_delete_file (Internal)
1108 */
1110 urlcache_header *header, entry_url *url_entry)
1111{
1114 LONG path_size = sizeof(path);
1115 DWORD err;
1116 WORD date, time;
1117
1118 if(!url_entry->local_name_off)
1119 goto succ;
1120
1122 (LPCSTR)url_entry+url_entry->local_name_off,
1123 url_entry->cache_dir, path, &path_size, FALSE))
1124 goto succ;
1125
1127 goto succ;
1128 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1129 if(date != url_entry->write_date || time != url_entry->write_time)
1130 goto succ;
1131
1134 return err;
1135
1136succ:
1137 if (url_entry->cache_dir < header->dirs_no)
1138 {
1139 if (header->directory_data[url_entry->cache_dir].files_no)
1140 header->directory_data[url_entry->cache_dir].files_no--;
1141 }
1142 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1143 {
1144 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1145 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1146 else
1147 header->exempt_usage.QuadPart = 0;
1148 }
1149 else
1150 {
1151 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1152 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1153 else
1154 header->cache_usage.QuadPart = 0;
1155 }
1156
1157 return ERROR_SUCCESS;
1158}
1159
1161{
1162 DWORD *leak_off;
1163 BOOL freed = FALSE;
1164
1165 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1166 while(*leak_off) {
1167 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1168
1170 *leak_off = url_entry->exempt_delta;
1171 urlcache_entry_free(header, &url_entry->header);
1172 freed = TRUE;
1173 }else {
1174 leak_off = &url_entry->exempt_delta;
1175 }
1176 }
1177
1178 return freed;
1179}
1180
1181/***********************************************************************
1182 * cache_container_clean_index (Internal)
1183 *
1184 * This function is meant to make place in index file by removing leaked
1185 * files entries and resizing the file.
1186 *
1187 * CAUTION: file view may get mapped to new memory
1188 *
1189 * RETURNS
1190 * ERROR_SUCCESS when new memory is available
1191 * error code otherwise
1192 */
1194{
1195 urlcache_header *header = *file_view;
1196 DWORD ret;
1197
1198 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1199
1201 return ERROR_SUCCESS;
1202
1204 WARN("index file has maximal size\n");
1206 }
1207
1209 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1210 if(ret != ERROR_SUCCESS)
1211 return ret;
1212 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1213 if(!header)
1214 return GetLastError();
1215
1216 UnmapViewOfFile(*file_view);
1217 *file_view = header;
1218 return ERROR_SUCCESS;
1219}
1220
1221/* Just like DosDateTimeToFileTime, except that it also maps the special
1222 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1223 */
1224static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1225 FILETIME *ft)
1226{
1227 if (!fatdate && !fattime)
1228 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1229 else
1230 DosDateTimeToFileTime(fatdate, fattime, ft);
1231}
1232
1233static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1234{
1235 URL_COMPONENTSA uc;
1236 DWORD len, part_len;
1237 WCHAR *host_name;
1238
1239 memset(&uc, 0, sizeof(uc));
1240 uc.dwStructSize = sizeof(uc);
1241 uc.dwHostNameLength = 1;
1242 if(!InternetCrackUrlA(url, 0, 0, &uc))
1244
1246 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1247
1248 if(!decoded_url)
1249 decoded_len = 0;
1250
1251 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1252 if(!len)
1253 return 0;
1254 if(decoded_url)
1255 decoded_len -= len;
1256
1257 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1258 if(!host_name)
1259 return 0;
1261 host_name, uc.dwHostNameLength)) {
1262 heap_free(host_name);
1263 return 0;
1264 }
1265 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1266 decoded_url ? decoded_url+len : NULL, decoded_len);
1267 heap_free(host_name);
1268 if(!part_len) {
1270 return 0;
1271 }
1272 len += part_len;
1273 if(decoded_url)
1274 decoded_len -= part_len;
1275
1276 part_len = MultiByteToWideChar(CP_UTF8, 0,
1278 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1279 if(!part_len)
1280 return 0;
1281 len += part_len;
1282
1283 return len;
1284}
1285
1286/***********************************************************************
1287 * urlcache_copy_entry (Internal)
1288 *
1289 * Copies an entry from the cache index file to the Win32 structure
1290 *
1291 * RETURNS
1292 * ERROR_SUCCESS if the buffer was big enough
1293 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1294 *
1295 */
1297 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1298{
1299 int url_len;
1300 DWORD size = sizeof(*entry_info);
1301
1302 if(*info_size >= size) {
1303 entry_info->lpHeaderInfo = NULL;
1304 entry_info->lpszFileExtension = NULL;
1305 entry_info->lpszLocalFileName = NULL;
1306 entry_info->lpszSourceUrlName = NULL;
1307 entry_info->CacheEntryType = url_entry->cache_entry_type;
1308 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1309 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1310 entry_info->dwHitRate = url_entry->hit_rate;
1311 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1312 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1313 entry_info->dwStructSize = sizeof(*entry_info);
1314 entry_info->dwUseCount = url_entry->use_count;
1315 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1316 entry_info->LastAccessTime = url_entry->access_time;
1317 entry_info->LastModifiedTime = url_entry->modification_time;
1318 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1319 }
1320
1321 if(unicode)
1322 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1323 else
1324 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1325 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1326
1327 if(*info_size >= size) {
1328 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1329
1330 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1331 if(unicode)
1332 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1333 else
1334 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1335 }
1336
1337 if(size%4 && size<*info_size)
1338 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1340
1341 if(url_entry->local_name_off) {
1342 LONG file_name_size;
1344 file_name = (LPSTR)entry_info+size;
1345 file_name_size = *info_size-size;
1346 if((unicode && urlcache_create_file_pathW(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1347 url_entry->cache_dir, (LPWSTR)file_name, &file_name_size, FALSE)) ||
1348 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1349 url_entry->cache_dir, file_name, &file_name_size))) {
1350 entry_info->lpszLocalFileName = file_name;
1351 }
1352 size += file_name_size;
1353
1354 if(size%4 && size<*info_size)
1355 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1357 }
1358
1359 if(url_entry->header_info_off) {
1360 DWORD header_len;
1361
1362 if(unicode)
1363 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1364 url_entry->header_info_size, NULL, 0);
1365 else
1366 header_len = url_entry->header_info_size;
1367 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1368
1369 if(*info_size >= size) {
1370 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1371 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1372 if(unicode)
1373 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1374 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1375 else
1376 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1377 }
1378 if(size%4 && size<*info_size)
1379 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1381 }
1382
1383 if(url_entry->file_extension_off) {
1384 int ext_len;
1385
1386 if(unicode)
1387 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1388 else
1389 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1390 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1391
1392 if(*info_size >= size) {
1393 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1394 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1395 if(unicode)
1396 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1397 else
1398 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1399 }
1400
1401 if(size%4 && size<*info_size)
1402 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1404 }
1405
1406 if(size > *info_size) {
1407 *info_size = size;
1409 }
1410 *info_size = size;
1411 return ERROR_SUCCESS;
1412}
1413
1414/***********************************************************************
1415 * urlcache_set_entry_info (Internal)
1416 *
1417 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1418 * according to the flags set by field_control.
1419 *
1420 * RETURNS
1421 * ERROR_SUCCESS if the buffer was big enough
1422 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1423 *
1424 */
1425static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1426{
1427 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1428 url_entry->access_time = entry_info->LastAccessTime;
1429 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1430 url_entry->cache_entry_type = entry_info->CacheEntryType;
1431 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1432 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1433 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1434 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1435 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1436 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1437 if (field_control & CACHE_ENTRY_HITRATE_FC)
1438 url_entry->hit_rate = entry_info->dwHitRate;
1439 if (field_control & CACHE_ENTRY_MODTIME_FC)
1440 url_entry->modification_time = entry_info->LastModifiedTime;
1441 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1442 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1443
1444 return ERROR_SUCCESS;
1445}
1446
1447/***********************************************************************
1448 * urlcache_hash_key (Internal)
1449 *
1450 * Returns the hash key for a given string
1451 *
1452 * RETURNS
1453 * hash key for the string
1454 *
1455 */
1457{
1458 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1459 * but the algorithm and result are not the same!
1460 */
1461 static const unsigned char lookupTable[256] =
1462 {
1463 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1464 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1465 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1466 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1467 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1468 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1469 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1470 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1471 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1472 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1473 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1474 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1475 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1476 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1477 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1478 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1479 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1480 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1481 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1482 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1483 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1484 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1485 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1486 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1487 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1488 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1489 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1490 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1491 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1492 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1493 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1494 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1495 };
1496 BYTE key[4];
1497 DWORD i;
1498
1499 for (i = 0; i < ARRAY_SIZE(key); i++)
1500 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1501
1502 for (lpszKey++; *lpszKey; lpszKey++)
1503 {
1504 for (i = 0; i < ARRAY_SIZE(key); i++)
1505 key[i] = lookupTable[*lpszKey ^ key[i]];
1506 }
1507
1508 return *(DWORD *)key;
1509}
1510
1512{
1513 if(!dwOffset)
1514 return NULL;
1516}
1517
1518static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1519{
1520 /* structure of hash table:
1521 * 448 entries divided into 64 blocks
1522 * each block therefore contains a chain of 7 key/offset pairs
1523 * how position in table is calculated:
1524 * 1. the url is hashed in helper function
1525 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1526 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1527 *
1528 * note:
1529 * there can be multiple hash tables in the file and the offset to
1530 * the next one is stored in the header of the hash table
1531 */
1532 DWORD key = urlcache_hash_key(lpszUrl);
1534 entry_hash_table* pHashEntry;
1535 DWORD id = 0;
1536
1538
1539 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1540 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1541 {
1542 int i;
1543 if (pHashEntry->id != id++)
1544 {
1545 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1546 continue;
1547 }
1548 /* make sure that it is in fact a hash entry */
1549 if (pHashEntry->header.signature != HASH_SIGNATURE)
1550 {
1551 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1552 continue;
1553 }
1554
1555 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1556 {
1557 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1558 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1559 {
1560 /* FIXME: we should make sure that this is the right element
1561 * before returning and claiming that it is. We can do this
1562 * by doing a simple compare between the URL we were given
1563 * and the URL stored in the entry. However, this assumes
1564 * we know the format of all the entries stored in the
1565 * hash table */
1566 *ppHashEntry = pHashElement;
1567 return TRUE;
1568 }
1569 }
1570 }
1571 return FALSE;
1572}
1573
1574/***********************************************************************
1575 * urlcache_hash_entry_set_flags (Internal)
1576 *
1577 * Sets special bits in hash key
1578 *
1579 * RETURNS
1580 * nothing
1581 *
1582 */
1583static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1584{
1585 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1586}
1587
1588/***********************************************************************
1589 * urlcache_hash_entry_delete (Internal)
1590 *
1591 * Searches all the hash tables in the index for the given URL and
1592 * then if found deletes the entry.
1593 *
1594 * RETURNS
1595 * TRUE if the entry was found
1596 * FALSE if the entry could not be found
1597 *
1598 */
1600{
1601 pHashEntry->key = HASHTABLE_DEL;
1602 return TRUE;
1603}
1604
1605/***********************************************************************
1606 * urlcache_hash_entry_create (Internal)
1607 *
1608 * Searches all the hash tables for a free slot based on the offset
1609 * generated from the hash key. If a free slot is found, the offset and
1610 * key are entered into the hash table.
1611 *
1612 * RETURNS
1613 * ERROR_SUCCESS if the entry was added
1614 * Any other Win32 error code if the entry could not be added
1615 *
1616 */
1617static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1618{
1619 /* see urlcache_find_hash_entry for structure of hash tables */
1620
1621 DWORD key = urlcache_hash_key(lpszUrl);
1623 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1624 DWORD id = 0;
1625 DWORD error;
1626
1627 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1628
1629 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1630 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1631 {
1632 int i;
1633 pHashPrev = pHashEntry;
1634
1635 if (pHashEntry->id != id++)
1636 {
1637 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1638 break;
1639 }
1640 /* make sure that it is in fact a hash entry */
1641 if (pHashEntry->header.signature != HASH_SIGNATURE)
1642 {
1643 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1644 break;
1645 }
1646
1647 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1648 {
1649 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1650 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1651 {
1652 pHashElement->key = key;
1653 pHashElement->offset = dwOffsetEntry;
1654 return ERROR_SUCCESS;
1655 }
1656 }
1657 }
1658 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1659 if (error != ERROR_SUCCESS)
1660 return error;
1661
1662 pHashEntry->hash_table[offset].key = key;
1663 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1664 return ERROR_SUCCESS;
1665}
1666
1667/***********************************************************************
1668 * urlcache_enum_hash_tables (Internal)
1669 *
1670 * Enumerates the hash tables in a container.
1671 *
1672 * RETURNS
1673 * TRUE if an entry was found
1674 * FALSE if there are no more tables to enumerate.
1675 *
1676 */
1678{
1679 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1680 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1681 {
1682 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1683 if ((*ppHashEntry)->id != *id)
1684 continue;
1685 /* make sure that it is in fact a hash entry */
1686 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1687 {
1688 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1689 (*id)++;
1690 continue;
1691 }
1692
1693 TRACE("hash table number %d found\n", *id);
1694 return TRUE;
1695 }
1696 return FALSE;
1697}
1698
1699/***********************************************************************
1700 * urlcache_enum_hash_table_entries (Internal)
1701 *
1702 * Enumerates entries in a hash table and returns the next non-free entry.
1703 *
1704 * RETURNS
1705 * TRUE if an entry was found
1706 * FALSE if the hash table is empty or there are no more entries to
1707 * enumerate.
1708 *
1709 */
1711 DWORD * index, const struct hash_entry **ppHashEntry)
1712{
1713 for (; *index < HASHTABLE_SIZE ; (*index)++)
1714 {
1715 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1716 continue;
1717
1718 *ppHashEntry = &pHashEntry->hash_table[*index];
1719 TRACE("entry found %d\n", *index);
1720 return TRUE;
1721 }
1722 TRACE("no more entries (%d)\n", *index);
1723 return FALSE;
1724}
1725
1726/***********************************************************************
1727 * cache_container_delete_dir (Internal)
1728 *
1729 * Erase a directory containing an URL cache.
1730 *
1731 * RETURNS
1732 * TRUE success, FALSE failure/aborted.
1733 *
1734 */
1736{
1738 WCHAR path[MAX_PATH + 1];
1739 SHFILEOPSTRUCTW shfos;
1740 int ret;
1741
1742 path_len = lstrlenW(lpszPath);
1743 if (path_len >= MAX_PATH)
1744 return FALSE;
1745 lstrcpyW(path, lpszPath);
1746 path[path_len + 1] = 0; /* double-NUL-terminate path */
1747
1748 shfos.hwnd = NULL;
1749 shfos.wFunc = FO_DELETE;
1750 shfos.pFrom = path;
1751 shfos.pTo = NULL;
1752 shfos.fFlags = FOF_NOCONFIRMATION;
1754 ret = SHFileOperationW(&shfos);
1755 if (ret)
1756 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1757 return !(ret || shfos.fAnyOperationsAborted);
1758}
1759
1760/***********************************************************************
1761 * urlcache_hash_entry_is_locked (Internal)
1762 *
1763 * Checks if entry is locked. Unlocks it if possible.
1764 */
1766{
1768 ULARGE_INTEGER acc_time, time;
1769
1770 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1771 return FALSE;
1772
1774 time.u.LowPart = cur_time.dwLowDateTime;
1775 time.u.HighPart = cur_time.dwHighDateTime;
1776
1777 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1778 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1779
1780 time.QuadPart -= acc_time.QuadPart;
1781
1782 /* check if entry was locked for at least a day */
1783 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1785 url_entry->use_count = 0;
1786 return FALSE;
1787 }
1788
1789 return TRUE;
1790}
1791
1792static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1793 DWORD *size, DWORD flags, BOOL unicode)
1794{
1796 struct hash_entry *hash_entry;
1797 const entry_url *url_entry;
1799 DWORD error;
1800
1801 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1802
1804 FIXME("ignoring unsupported flags: %x\n", flags);
1805
1807 if(error != ERROR_SUCCESS) {
1809 return FALSE;
1810 }
1811
1813 if(error != ERROR_SUCCESS) {
1815 return FALSE;
1816 }
1817
1819 return FALSE;
1820
1823 WARN("entry %s not found!\n", debugstr_a(url));
1825 return FALSE;
1826 }
1827
1828 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1829 if(url_entry->header.signature != URL_SIGNATURE) {
1830 FIXME("Trying to retrieve entry of unknown format %s\n",
1831 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1834 return FALSE;
1835 }
1836
1837 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1838 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1839 url_entry->header_info_off, url_entry->header_info_size));
1840
1844 return FALSE;
1845 }
1846
1847 if(size) {
1848 if(!entry_info)
1849 *size = 0;
1850
1851 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1852 if(error != ERROR_SUCCESS) {
1855 return FALSE;
1856 }
1857 if(url_entry->local_name_off)
1858 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1859 }
1860
1862 return TRUE;
1863}
1864
1865/***********************************************************************
1866 * GetUrlCacheEntryInfoExA (WININET.@)
1867 *
1868 */
1870 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1871 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1872 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1873{
1874 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1875 ERR("Reserved value was not 0\n");
1877 return FALSE;
1878 }
1879
1880 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1881 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1882}
1883
1884/***********************************************************************
1885 * GetUrlCacheEntryInfoA (WININET.@)
1886 *
1887 */
1889 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1890 LPDWORD lpdwCacheEntryInfoBufferSize)
1891{
1892 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1893 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1894}
1895
1896static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1897{
1898 URL_COMPONENTSW uc;
1899 DWORD len, part_len;
1900 WCHAR *punycode;
1901
1902 TRACE("%s\n", debugstr_w(url));
1903
1904 memset(&uc, 0, sizeof(uc));
1905 uc.dwStructSize = sizeof(uc);
1906 uc.dwHostNameLength = 1;
1907 if(!InternetCrackUrlW(url, 0, 0, &uc))
1909
1911 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1912
1914 encoded_url, encoded_len, NULL, NULL);
1915 if(!len)
1916 return 0;
1917 if(encoded_url)
1918 encoded_len -= len;
1919
1920 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1921 if(!part_len) {
1923 return 0;
1924 }
1925
1926 punycode = heap_alloc(part_len*sizeof(WCHAR));
1927 if(!punycode)
1928 return 0;
1929
1930 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1931 if(!part_len) {
1932 heap_free(punycode);
1933 return 0;
1934 }
1935
1936 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1937 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1938 heap_free(punycode);
1939 if(!part_len)
1940 return 0;
1941 if(encoded_url)
1942 encoded_len -= part_len;
1943 len += part_len;
1944
1946 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1947 if(!part_len)
1948 return 0;
1949 len += part_len;
1950
1951 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1952 return len;
1953}
1954
1955static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1956{
1957 DWORD encoded_len;
1958 char *ret;
1959
1960 encoded_len = urlcache_encode_url(url, NULL, 0);
1961 if(!encoded_len)
1962 return FALSE;
1963
1964 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1965 if(!ret)
1966 return FALSE;
1967
1968 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1969 if(!encoded_len) {
1970 heap_free(ret);
1971 return FALSE;
1972 }
1973
1974 *encoded_url = ret;
1975 return TRUE;
1976}
1977
1978/***********************************************************************
1979 * GetUrlCacheEntryInfoExW (WININET.@)
1980 *
1981 */
1983 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1984 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1985 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1986{
1987 char *url;
1988 BOOL ret;
1989
1990 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1991 ERR("Reserved value was not 0\n");
1993 return FALSE;
1994 }
1995
1996 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1997 dwFlags &= ~GET_INSTALLED_ENTRY;
1998
1999 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2000 return FALSE;
2001
2002 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
2003 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
2004 heap_free(url);
2005 return ret;
2006}
2007
2008/***********************************************************************
2009 * GetUrlCacheEntryInfoW (WININET.@)
2010 *
2011 */
2013 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2014 LPDWORD lpdwCacheEntryInfoBufferSize)
2015{
2016 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
2017 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
2018}
2019
2020/***********************************************************************
2021 * SetUrlCacheEntryInfoA (WININET.@)
2022 */
2024 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2025 DWORD dwFieldControl)
2026{
2028 struct hash_entry *pHashEntry;
2030 cache_container *pContainer;
2031 DWORD error;
2032
2033 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2034
2035 error = cache_containers_find(lpszUrlName, &pContainer);
2036 if (error != ERROR_SUCCESS)
2037 {
2039 return FALSE;
2040 }
2041
2043 if (error != ERROR_SUCCESS)
2044 {
2046 return FALSE;
2047 }
2048
2049 if (!(pHeader = cache_container_lock_index(pContainer)))
2050 return FALSE;
2051
2052 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2053 {
2055 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2057 return FALSE;
2058 }
2059
2060 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2061 if (pEntry->signature != URL_SIGNATURE)
2062 {
2063 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2066 return FALSE;
2067 }
2068
2069 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2070
2072
2073 return TRUE;
2074}
2075
2076/***********************************************************************
2077 * SetUrlCacheEntryInfoW (WININET.@)
2078 */
2080 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2081 DWORD dwFieldControl)
2082{
2083 char *url;
2084 BOOL ret;
2085
2086 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2087 return FALSE;
2088
2089 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2090 heap_free(url);
2091 return ret;
2092}
2093
2094static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2095{
2097 struct hash_entry *hash_entry;
2098 entry_url *url_entry;
2100 DWORD error;
2101
2102 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2103
2104 if(!url || !size || (!entry_info && *size)) {
2106 return FALSE;
2107 }
2108
2110 if(error != ERROR_SUCCESS) {
2112 return FALSE;
2113 }
2114
2116 if (error != ERROR_SUCCESS) {
2118 return FALSE;
2119 }
2120
2122 return FALSE;
2123
2126 TRACE("entry %s not found!\n", debugstr_a(url));
2128 return FALSE;
2129 }
2130
2131 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2132 if(url_entry->header.signature != URL_SIGNATURE) {
2133 FIXME("Trying to retrieve entry of unknown format %s\n",
2134 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2137 return FALSE;
2138 }
2139
2140 if(!url_entry->local_name_off) {
2143 return FALSE;
2144 }
2145
2146 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2147 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2148 url_entry->header_info_size));
2149
2151 size, url_entry, unicode);
2152 if(error != ERROR_SUCCESS) {
2155 return FALSE;
2156 }
2157 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2158
2159 url_entry->hit_rate++;
2160 url_entry->use_count++;
2163
2165
2166 return TRUE;
2167}
2168
2169/***********************************************************************
2170 * RetrieveUrlCacheEntryFileA (WININET.@)
2171 *
2172 */
2174 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2175 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2176{
2177 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2178 lpdwCacheEntryInfoBufferSize, FALSE);
2179}
2180
2181/***********************************************************************
2182 * RetrieveUrlCacheEntryFileW (WININET.@)
2183 *
2184 */
2186 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2187 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2188{
2189 char *url;
2190 BOOL ret;
2191
2192 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2193 return FALSE;
2194
2195 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2196 lpdwCacheEntryInfoBufferSize, TRUE);
2197 heap_free(url);
2198 return ret;
2199}
2200
2202 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2203{
2205 entry_url * pUrlEntry;
2206
2207 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2208 if (pEntry->signature != URL_SIGNATURE)
2209 {
2210 FIXME("Trying to delete entry of unknown format %s\n",
2211 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2213 return FALSE;
2214 }
2215
2216 pUrlEntry = (entry_url *)pEntry;
2217 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2218 {
2219 TRACE("Trying to delete locked entry\n");
2222 return FALSE;
2223 }
2224
2225 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2226 {
2228 }
2229 else
2230 {
2231 /* Add entry to leaked files list */
2232 pUrlEntry->header.signature = LEAK_SIGNATURE;
2234 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2235 }
2236
2237 urlcache_hash_entry_delete(pHashEntry);
2238 return TRUE;
2239}
2240
2244{
2245 FreeUrlCacheSpaceW(NULL, 20, 0);
2247 return 0;
2248}
2249
2250static void handle_full_cache(void)
2251{
2255 }
2256}
2257
2258/* Enumerates entries in cache, allows cache unlocking between calls. */
2259static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2261{
2262 entry_hash_table *hashtable_entry;
2263
2264 *hash_entry = NULL;
2265 *entry = NULL;
2266
2267 if(!*hash_table_off) {
2268 *hash_table_off = header->hash_table_off;
2269 *hash_table_entry = 0;
2270
2271 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2272 }else {
2273 if(*hash_table_off >= header->size) {
2274 *hash_table_off = 0;
2275 return FALSE;
2276 }
2277
2278 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2279 }
2280
2281 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2282 *hash_table_off = 0;
2283 return FALSE;
2284 }
2285
2286 while(1) {
2287 if(*hash_table_entry >= HASHTABLE_SIZE) {
2288 *hash_table_off = hashtable_entry->next;
2289 if(!*hash_table_off) {
2290 *hash_table_off = 0;
2291 return FALSE;
2292 }
2293
2294 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2295 *hash_table_entry = 0;
2296 }
2297
2298 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2299 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2300 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2301 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2302 (*hash_table_entry)++;
2303 return TRUE;
2304 }
2305
2306 (*hash_table_entry)++;
2307 }
2308
2309 *hash_table_off = 0;
2310 return FALSE;
2311}
2312
2313/* Rates an urlcache entry to determine if it can be deleted.
2314 *
2315 * Score 0 means that entry can safely be removed, the bigger rating
2316 * the smaller chance of entry being removed.
2317 * DWORD_MAX means that entry can't be deleted at all.
2318 *
2319 * Rating system is currently not fully compatible with native implementation.
2320 */
2322{
2323 ULARGE_INTEGER time, access_time;
2324 DWORD rating;
2325
2326 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2327 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2328
2329 time.u.LowPart = cur_time->dwLowDateTime;
2330 time.u.HighPart = cur_time->dwHighDateTime;
2331
2332 /* Don't touch entries that were added less than 10 minutes ago */
2333 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2334 return -1;
2335
2336 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2337 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2338 return -1;
2339
2340 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2341 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2342
2343 if(url_entry->hit_rate > 100)
2344 rating += 100;
2345 else
2346 rating += url_entry->hit_rate;
2347
2348 return rating;
2349}
2350
2351static int __cdecl dword_cmp(const void *p1, const void *p2)
2352{
2353 return *(const DWORD*)p1 - *(const DWORD*)p2;
2354}
2355
2356/***********************************************************************
2357 * FreeUrlCacheSpaceW (WININET.@)
2358 *
2359 * Frees up some cache.
2360 *
2361 * PARAMETERS
2362 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2363 * size [I] Percentage of the cache that should be free.
2364 * filter [I] Which entries can't be deleted (CacheEntryType)
2365 *
2366 * RETURNS
2367 * TRUE success. FALSE failure.
2368 *
2369 * IMPLEMENTATION
2370 * This implementation just retrieves the path of the cache directory, and
2371 * deletes its contents from the filesystem. The correct approach would
2372 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2373 */
2375{
2378
2379 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2380
2381 if(size<1 || size>100) {
2383 return FALSE;
2384 }
2385
2386 if(cache_path) {
2387 path_len = lstrlenW(cache_path);
2388 if(cache_path[path_len-1] == '\\')
2389 path_len--;
2390 }else {
2391 path_len = 0;
2392 }
2393
2394 if(size==100 && !filter) {
2396 {
2397 /* When cache_path==NULL only clean Temporary Internet Files */
2398 if((!path_len && container->cache_prefix[0]==0) ||
2399 (path_len && !wcsnicmp(container->path, cache_path, path_len) &&
2400 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2401 {
2402 BOOL ret_del;
2403
2405
2406 /* unlock, delete, recreate and lock cache */
2408 ret_del = cache_container_delete_dir(container->path);
2410
2411 ReleaseMutex(container->mutex);
2412 if(!ret_del || (err != ERROR_SUCCESS))
2413 return FALSE;
2414 }
2415 }
2416
2417 return TRUE;
2418 }
2419
2421 {
2423 struct hash_entry *hash_entry;
2425 entry_url *url_entry;
2426 ULONGLONG desired_size, cur_size;
2427 DWORD delete_factor, hash_table_off, hash_table_entry;
2428 DWORD rate[100], rate_no;
2430
2431 if((path_len || container->cache_prefix[0]!=0) &&
2432 (!path_len || wcsnicmp(container->path, cache_path, path_len) ||
2433 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2434 continue;
2435
2437 if(err != ERROR_SUCCESS)
2438 continue;
2439
2441 if(!header)
2442 continue;
2443
2445
2446 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2447 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2448 if(cur_size <= desired_size)
2449 delete_factor = 0;
2450 else
2451 delete_factor = (cur_size-desired_size)*100/cur_size;
2452
2453 if(!delete_factor) {
2455 continue;
2456 }
2457
2458 hash_table_off = 0;
2459 hash_table_entry = 0;
2460 rate_no = 0;
2462 while(rate_no < ARRAY_SIZE(rate) &&
2463 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2464 if(entry->signature != URL_SIGNATURE) {
2465 WARN("only url entries are currently supported\n");
2466 continue;
2467 }
2468
2469 url_entry = (entry_url*)entry;
2470 if(url_entry->cache_entry_type & filter)
2471 continue;
2472
2473 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2474 if(rate[rate_no] != -1)
2475 rate_no++;
2476 }
2477
2478 if(!rate_no) {
2479 TRACE("nothing to delete\n");
2481 continue;
2482 }
2483
2484 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2485
2486 delete_factor = delete_factor*rate_no/100;
2487 delete_factor = rate[delete_factor];
2488 TRACE("deleting files with rating %d or less\n", delete_factor);
2489
2490 hash_table_off = 0;
2491 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2492 if(entry->signature != URL_SIGNATURE)
2493 continue;
2494
2495 url_entry = (entry_url*)entry;
2496 if(url_entry->cache_entry_type & filter)
2497 continue;
2498
2499 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2500 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2502
2503 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2504 break;
2505
2506 /* Allow other threads to use cache while cleaning */
2509 TRACE("got dll_unload_event - finishing\n");
2510 return TRUE;
2511 }
2512 Sleep(0);
2514 }
2515 }
2516
2517 TRACE("cache size after cleaning 0x%s/0x%s\n",
2518 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2519 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2521 }
2522
2523 return TRUE;
2524}
2525
2526/***********************************************************************
2527 * FreeUrlCacheSpaceA (WININET.@)
2528 *
2529 * See FreeUrlCacheSpaceW.
2530 */
2532{
2533 BOOL ret = FALSE;
2534 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2535 if (lpszCachePath == NULL || path != NULL)
2536 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2537 heap_free(path);
2538 return ret;
2539}
2540
2541/***********************************************************************
2542 * UnlockUrlCacheEntryFileA (WININET.@)
2543 *
2544 */
2546{
2548 struct hash_entry *pHashEntry;
2550 entry_url * pUrlEntry;
2551 cache_container *pContainer;
2552 DWORD error;
2553
2554 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2555
2556 if (dwReserved)
2557 {
2558 ERR("dwReserved != 0\n");
2560 return FALSE;
2561 }
2562
2563 error = cache_containers_find(lpszUrlName, &pContainer);
2564 if (error != ERROR_SUCCESS)
2565 {
2567 return FALSE;
2568 }
2569
2571 if (error != ERROR_SUCCESS)
2572 {
2574 return FALSE;
2575 }
2576
2577 if (!(pHeader = cache_container_lock_index(pContainer)))
2578 return FALSE;
2579
2580 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2581 {
2583 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
2585 return FALSE;
2586 }
2587
2588 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2589 if (pEntry->signature != URL_SIGNATURE)
2590 {
2592 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2594 return FALSE;
2595 }
2596
2597 pUrlEntry = (entry_url *)pEntry;
2598
2599 if (pUrlEntry->use_count == 0)
2600 {
2602 return FALSE;
2603 }
2604 pUrlEntry->use_count--;
2605 if (!pUrlEntry->use_count)
2606 {
2609 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2610 }
2611
2613
2614 return TRUE;
2615}
2616
2617/***********************************************************************
2618 * UnlockUrlCacheEntryFileW (WININET.@)
2619 *
2620 */
2622{
2623 char *url;
2624 BOOL ret;
2625
2626 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2627 return FALSE;
2628
2630 heap_free(url);
2631 return ret;
2632}
2633
2634static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2635{
2638 char file_name[MAX_PATH];
2639 WCHAR extW[MAX_PATH];
2640 BYTE cache_dir;
2641 LONG full_path_len, ext_len = 0;
2642 BOOL generate_name = FALSE;
2643 DWORD error;
2644 HANDLE file;
2645 FILETIME ft;
2646 URL_COMPONENTSA uc;
2647 int i;
2648
2649 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2650
2651 memset(&uc, 0, sizeof(uc));
2652 uc.dwStructSize = sizeof(uc);
2653 uc.dwUrlPathLength = 1;
2654 uc.dwExtraInfoLength = 1;
2655 if(!InternetCrackUrlA(url, 0, 0, &uc))
2656 uc.dwUrlPathLength = 0;
2657
2658 if(!uc.dwUrlPathLength) {
2659 file_name[0] = 0;
2660 }else {
2661 char *p, *e;
2662
2663 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2664 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2665 p--;
2666 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2667 e = p-1;
2668 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2669 p--;
2670 }
2671
2672 if(e-p >= MAX_PATH)
2673 e = p+MAX_PATH-1;
2674 memcpy(file_name, p, e-p);
2675 file_name[e-p] = 0;
2676
2677 for(p=file_name; *p; p++) {
2678 switch(*p) {
2679 case '<': case '>':
2680 case ':': case '"':
2681 case '|': case '?':
2682 case '*':
2683 *p = '_'; break;
2684 default: break;
2685 }
2686 }
2687 }
2688
2689 if(!file_name[0])
2690 generate_name = TRUE;
2691
2693 if(error != ERROR_SUCCESS) {
2695 return FALSE;
2696 }
2697
2699 if(error != ERROR_SUCCESS) {
2701 return FALSE;
2702 }
2703
2705 return FALSE;
2706
2707 if(header->dirs_no)
2708 cache_dir = (BYTE)(rand() % header->dirs_no);
2709 else
2710 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2711
2712 full_path_len = MAX_PATH * sizeof(WCHAR);
2713 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len, TRUE)) {
2714 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2715 debugstr_a(file_name), full_path_len);
2717 return FALSE;
2718 }
2719 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2720
2722
2723 if(ext) {
2724 WCHAR *p;
2725
2726 extW[0] = '.';
2727 ext_len = MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2728
2729 for(p=extW; *p; p++) {
2730 switch(*p) {
2731 case '<': case '>':
2732 case ':': case '"':
2733 case '|': case '?':
2734 case '*':
2735 *p = '_'; break;
2736 default: break;
2737 }
2738 }
2739 if(p[-1]==' ' || p[-1]=='.')
2740 p[-1] = '_';
2741 }else {
2742 extW[0] = '\0';
2743 }
2744
2745 if(!generate_name && full_path_len+5+ext_len>=MAX_PATH) { /* strlen("[255]") = 5 */
2746 full_path_len = MAX_PATH-5-ext_len-1;
2747 }
2748
2749 for(i=0; i<255 && !generate_name; i++) {
2750 wsprintfW(full_path+full_path_len, L"[%u]%s", i, extW);
2751
2752 TRACE("Trying: %s\n", debugstr_w(full_path));
2753 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2754 if(file != INVALID_HANDLE_VALUE) {
2756 return TRUE;
2757 }
2758 }
2759
2760 if(full_path_len+8+ext_len >= MAX_PATH)
2761 full_path_len = MAX_PATH-8-ext_len-1;
2762
2763 /* Try to generate random name */
2765 lstrcpyW(full_path+full_path_len+8, extW);
2766
2767 for(i=0; i<255; i++) {
2768 int j;
2770 n <<= 32;
2771 n += ft.dwLowDateTime;
2772 n ^= (ULONGLONG)i<<48;
2773
2774 for(j=0; j<8; j++) {
2775 int r = (n % 36);
2776 n /= 37;
2777 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2778 }
2779
2780 TRACE("Trying: %s\n", debugstr_w(full_path));
2781 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2782 if(file != INVALID_HANDLE_VALUE) {
2784 return TRUE;
2785 }
2786 }
2787
2788 WARN("Could not find a unique filename\n");
2789 return FALSE;
2790}
2791
2792/***********************************************************************
2793 * CreateUrlCacheEntryA (WININET.@)
2794 *
2795 */
2796BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2797 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2798{
2800
2801 if(dwReserved)
2802 FIXME("dwReserved 0x%08x\n", dwReserved);
2803
2804 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2805 return FALSE;
2806
2807 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2808 return FALSE;
2809 return TRUE;
2810}
2811/***********************************************************************
2812 * CreateUrlCacheEntryW (WININET.@)
2813 *
2814 */
2815BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2816 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2817{
2818 char *url, *ext = NULL;
2819 BOOL ret;
2820
2821 if(dwReserved)
2822 FIXME("dwReserved 0x%08x\n", dwReserved);
2823
2824 if(lpszFileExtension) {
2825 ext = heap_strdupWtoUTF8(lpszFileExtension);
2826 if(!ext)
2827 return FALSE;
2828 }
2829
2830 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2831 heap_free(ext);
2832 return FALSE;
2833 }
2834
2835 ret = urlcache_entry_create(url, ext, lpszFileName);
2836 heap_free(ext);
2837 heap_free(url);
2838 return ret;
2839}
2840
2841static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2842 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2843 BYTE *header_info, DWORD header_size, const char *file_ext,
2844 const char *original_url)
2845{
2848 struct hash_entry *hash_entry;
2850 entry_url *url_entry;
2851 DWORD url_entry_offset;
2852 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2853 DWORD file_name_off = 0;
2854 DWORD header_info_off = 0;
2855 DWORD file_ext_off = 0;
2856 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2858 BYTE dir_id;
2859 char file_name_no_container[MAX_PATH];
2860 char *local_file_name = 0;
2861 DWORD hit_rate = 0;
2862 DWORD exempt_delta = 0;
2863 DWORD error;
2864
2865 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2866 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2867
2868 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2870 return FALSE;
2871 }
2872 if(original_url)
2873 WARN(": original_url ignored\n");
2874
2875 memset(&file_attr, 0, sizeof(file_attr));
2876 if(file_name) {
2878 return FALSE;
2879 }
2880 file_size.u.LowPart = file_attr.nFileSizeLow;
2881 file_size.u.HighPart = file_attr.nFileSizeHigh;
2882
2884 if(error != ERROR_SUCCESS) {
2886 return FALSE;
2887 }
2888
2890 if(error != ERROR_SUCCESS) {
2892 return FALSE;
2893 }
2894
2896 return FALSE;
2897
2899 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2900
2902 TRACE("Trying to overwrite locked entry\n");
2905 return FALSE;
2906 }
2907
2908 hit_rate = url_entry->hit_rate;
2909 exempt_delta = url_entry->exempt_delta;
2911 }
2912
2913 if(header->dirs_no)
2914 dir_id = 0;
2915 else
2917
2918 if(file_name) {
2919 BOOL bFound = FALSE;
2920
2921 if(wcsncmp(file_name, container->path, lstrlenW(container->path))) {
2922 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2925 return FALSE;
2926 }
2927
2928 /* skip container path prefix */
2929 file_name += lstrlenW(container->path);
2930
2931 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2932 local_file_name = file_name_no_container;
2933
2934 if(header->dirs_no) {
2935 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2936 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2937 bFound = TRUE;
2938 break;
2939 }
2940 }
2941
2942 if(!bFound) {
2943 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2946 return FALSE;
2947 }
2948
2949 file_name += DIR_LENGTH + 1;
2950 local_file_name += DIR_LENGTH + 1;
2951 }
2952 }
2953
2954 size = DWORD_ALIGN(size + strlen(url) + 1);
2955 if(file_name) {
2956 file_name_off = size;
2957 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2958 }
2959 if(header_info && header_size) {
2960 header_info_off = size;
2961 size = DWORD_ALIGN(size + header_size);
2962 }
2963 if(file_ext && (file_ext_off = strlen(file_ext))) {
2964 DWORD len = file_ext_off;
2965
2966 file_ext_off = size;
2967 size = DWORD_ALIGN(size + len + 1);
2968 }
2969
2970 /* round up to next block */
2971 if(size % BLOCKSIZE) {
2972 size -= size % BLOCKSIZE;
2973 size += BLOCKSIZE;
2974 }
2975
2977 while(error == ERROR_HANDLE_DISK_FULL) {
2979 if(error == ERROR_SUCCESS)
2981 }
2982 if(error != ERROR_SUCCESS) {
2985 return FALSE;
2986 }
2987
2988 /* FindFirstFreeEntry fills in blocks used */
2989 url_entry = (entry_url *)entry;
2990 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2991 url_entry->header.signature = URL_SIGNATURE;
2992 url_entry->cache_dir = dir_id;
2993 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2994 url_entry->header_info_size = header_size;
2995 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2996 /* Sticky entries have a default exempt time of one day */
2997 exempt_delta = 86400;
2998 }
2999 url_entry->exempt_delta = exempt_delta;
3000 url_entry->hit_rate = hit_rate+1;
3001 url_entry->file_extension_off = file_ext_off;
3002 url_entry->header_info_off = header_info_off;
3003 url_entry->local_name_off = file_name_off;
3004 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
3005 url_entry->size.QuadPart = file_size.QuadPart;
3006 url_entry->use_count = 0;
3008 url_entry->modification_time = modify_time;
3009 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
3010 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
3011 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
3012
3013 /*** Unknowns ***/
3014 url_entry->unk1 = 0;
3015 url_entry->unk2 = 0;
3016 url_entry->unk3 = 0x60;
3017 url_entry->unk4 = 0;
3018 url_entry->unk5 = 0x1010;
3019 url_entry->unk7 = 0;
3020 url_entry->unk8 = 0;
3021
3022
3023 strcpy((LPSTR)url_entry + url_entry->url_off, url);
3024 if(file_name_off)
3025 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
3026 if(header_info_off)
3027 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
3028 if(file_ext_off)
3029 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
3030
3032 while(error == ERROR_HANDLE_DISK_FULL) {
3034 if(error == ERROR_SUCCESS) {
3035 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3037 url_entry_offset, HASHTABLE_URL);
3038 }
3039 }
3040 if(error != ERROR_SUCCESS) {
3041 urlcache_entry_free(header, &url_entry->header);
3044 return FALSE;
3045 }
3046
3047 if(url_entry->cache_dir < header->dirs_no)
3048 header->directory_data[url_entry->cache_dir].files_no++;
3049 if(entry_type & STICKY_CACHE_ENTRY)
3050 header->exempt_usage.QuadPart += file_size.QuadPart;
3051 else
3052 header->cache_usage.QuadPart += file_size.QuadPart;
3053 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3055
3057 return TRUE;
3058}
3059
3060/***********************************************************************
3061 * CommitUrlCacheEntryA (WININET.@)
3062 */
3063BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3064 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3065 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3066{
3067 WCHAR *file_name = NULL;
3068 BOOL ret;
3069
3070 if(lpszLocalFileName) {
3071 file_name = heap_strdupAtoW(lpszLocalFileName);
3072 if(!file_name)
3073 return FALSE;
3074 }
3075
3076 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3077 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3079 return ret;
3080}
3081
3082/***********************************************************************
3083 * CommitUrlCacheEntryW (WININET.@)
3084 */
3085BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3086 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3087 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3088{
3089 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3090 BOOL ret;
3091
3092 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3093 return FALSE;
3094
3095 if(lpHeaderInfo) {
3096 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3097 if(!header_info) {
3098 heap_free(url);
3099 return FALSE;
3100 }
3101 dwHeaderSize = strlen(header_info);
3102 }
3103
3104 if(lpszFileExtension) {
3105 file_ext = heap_strdupWtoA(lpszFileExtension);
3106 if(!file_ext) {
3107 heap_free(url);
3108 heap_free(header_info);
3109 return FALSE;
3110 }
3111 }
3112
3113 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3114 heap_free(url);
3115 heap_free(header_info);
3116 heap_free(file_ext);
3117 return FALSE;
3118 }
3119
3120 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3121 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3122 heap_free(url);
3123 heap_free(header_info);
3124 heap_free(file_ext);
3125 heap_free(original_url);
3126 return ret;
3127}
3128
3129/***********************************************************************
3130 * ReadUrlCacheEntryStream (WININET.@)
3131 *
3132 */
3134 IN HANDLE hUrlCacheStream,
3135 IN DWORD dwLocation,
3137 IN OUT LPDWORD lpdwLen,
3139 )
3140{
3141 /* Get handle to file from 'stream' */
3142 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3143
3144 if (dwReserved != 0)
3145 {
3146 ERR("dwReserved != 0\n");
3148 return FALSE;
3149 }
3150
3151 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3152 {
3154 return FALSE;
3155 }
3156
3157 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3158 return FALSE;
3159 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3160}
3161
3162/***********************************************************************
3163 * RetrieveUrlCacheEntryStreamA (WININET.@)
3164 *
3165 */
3167 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3168 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3169{
3170 /* NOTE: this is not the same as the way that the native
3171 * version allocates 'stream' handles. I did it this way
3172 * as it is much easier and no applications should depend
3173 * on this behaviour. (Native version appears to allocate
3174 * indices into a table)
3175 */
3177 HANDLE file;
3178
3179 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3180 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3181
3182 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3183 lpdwCacheEntryInfoBufferSize, dwReserved))
3184 return NULL;
3185
3187 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3188 if(file == INVALID_HANDLE_VALUE) {
3189 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3190 return NULL;
3191 }
3192
3193 /* allocate handle storage space */
3194 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3195 if(!stream) {
3197 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3199 return NULL;
3200 }
3201
3202 stream->file = file;
3203 strcpy(stream->url, lpszUrlName);
3204 return stream;
3205}
3206
3207/***********************************************************************
3208 * RetrieveUrlCacheEntryStreamW (WININET.@)
3209 *
3210 */
3212 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3213 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3214{
3215 DWORD len;
3216 /* NOTE: this is not the same as the way that the native
3217 * version allocates 'stream' handles. I did it this way
3218 * as it is much easier and no applications should depend
3219 * on this behaviour. (Native version appears to allocate
3220 * indices into a table)
3221 */
3223 HANDLE file;
3224
3225 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3226 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3227
3228 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3229 return NULL;
3230
3231 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3232 lpdwCacheEntryInfoBufferSize, dwReserved))
3233 return NULL;
3234
3236 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3237 if(file == INVALID_HANDLE_VALUE) {
3238 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3239 return NULL;
3240 }
3241
3242 /* allocate handle storage space */
3243 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3244 if(!stream) {
3246 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3248 return NULL;
3249 }
3250
3251 stream->file = file;
3252 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3254 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3256 return NULL;
3257 }
3258 return stream;
3259}
3260
3261/***********************************************************************
3262 * UnlockUrlCacheEntryStream (WININET.@)
3263 *
3264 */
3266 IN HANDLE hUrlCacheStream,
3268)
3269{
3270 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3271
3272 if (dwReserved != 0)
3273 {
3274 ERR("dwReserved != 0\n");
3276 return FALSE;
3277 }
3278
3279 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3280 {
3282 return FALSE;
3283 }
3284
3285 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3286 return FALSE;
3287
3288 CloseHandle(pStream->file);
3289 heap_free(pStream);
3290 return TRUE;
3291}
3292
3293
3294/***********************************************************************
3295 * DeleteUrlCacheEntryA (WININET.@)
3296 *
3297 */
3299{
3300 cache_container *pContainer;
3302 struct hash_entry *pHashEntry;
3303 DWORD error;
3304 BOOL ret;
3305
3306 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3307
3308 error = cache_containers_find(lpszUrlName, &pContainer);
3309 if (error != ERROR_SUCCESS)
3310 {
3312 return FALSE;
3313 }
3314
3316 if (error != ERROR_SUCCESS)
3317 {
3319 return FALSE;
3320 }
3321
3322 if (!(pHeader = cache_container_lock_index(pContainer)))
3323 return FALSE;
3324
3325 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3326 {
3328 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
3330 return FALSE;
3331 }
3332
3333 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3334
3336
3337 return ret;
3338}
3339
3340/***********************************************************************
3341 * DeleteUrlCacheEntryW (WININET.@)
3342 *
3343 */
3345{
3346 char *url;
3347 BOOL ret;
3348
3349 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3350 return FALSE;
3351
3353 heap_free(url);
3354 return ret;
3355}
3356
3358{
3359 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3360 return TRUE;
3361}
3362
3364{
3365 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3366 return TRUE;
3367}
3368
3369/***********************************************************************
3370 * CreateCacheContainerA (WININET.@)
3371 */
3373 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3374{
3375 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3376 d1, d2, d3, d4, d5, d6, d7, d8);
3377 return TRUE;
3378}
3379
3380/***********************************************************************
3381 * CreateCacheContainerW (WININET.@)
3382 */
3384 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3385{
3386 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3387 d1, d2, d3, d4, d5, d6, d7, d8);
3388 return TRUE;
3389}
3390
3391/***********************************************************************
3392 * FindFirstUrlCacheContainerA (WININET.@)
3393 */
3395{
3396 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3397 return NULL;
3398}
3399
3400/***********************************************************************
3401 * FindFirstUrlCacheContainerW (WININET.@)
3402 */
3404{
3405 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3406 return NULL;
3407}
3408
3409/***********************************************************************
3410 * FindNextUrlCacheContainerA (WININET.@)
3411 */
3413{
3414 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3415 return FALSE;
3416}
3417
3418/***********************************************************************
3419 * FindNextUrlCacheContainerW (WININET.@)
3420 */
3422{
3423 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3424 return FALSE;
3425}
3426
3428 LPCSTR lpszUrlSearchPattern,
3429 DWORD dwFlags,
3430 DWORD dwFilter,
3431 GROUPID GroupId,
3432 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3433 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3434 LPVOID lpReserved,
3435 LPDWORD pcbReserved2,
3436 LPVOID lpReserved3
3437)
3438{
3439 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3440 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3441 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3443 return NULL;
3444}
3445
3447 LPCWSTR lpszUrlSearchPattern,
3448 DWORD dwFlags,
3449 DWORD dwFilter,
3450 GROUPID GroupId,
3451 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3452 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3453 LPVOID lpReserved,
3454 LPDWORD pcbReserved2,
3455 LPVOID lpReserved3
3456)
3457{
3458 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3459 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3460 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3462 return NULL;
3463}
3464
3465/***********************************************************************
3466 * FindFirstUrlCacheEntryA (WININET.@)
3467 *
3468 */
3470 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3471{
3472 find_handle *pEntryHandle;
3473
3474 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3475
3476 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3477 if (!pEntryHandle)
3478 return NULL;
3479
3481 if (lpszUrlSearchPattern)
3482 {
3483 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3484 if (!pEntryHandle->url_search_pattern)
3485 {
3486 heap_free(pEntryHandle);
3487 return NULL;
3488 }
3489 }
3490 else
3491 pEntryHandle->url_search_pattern = NULL;
3492 pEntryHandle->container_idx = 0;
3493 pEntryHandle->hash_table_idx = 0;
3494 pEntryHandle->hash_entry_idx = 0;
3495
3496 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3497 {
3498 heap_free(pEntryHandle);
3499 return NULL;
3500 }
3501 return pEntryHandle;
3502}
3503
3504/***********************************************************************
3505 * FindFirstUrlCacheEntryW (WININET.@)
3506 *
3507 */
3509 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3510{
3511 find_handle *pEntryHandle;
3512
3513 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3514
3515 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3516 if (!pEntryHandle)
3517 return NULL;
3518
3520 if (lpszUrlSearchPattern)
3521 {
3522 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3523 if (!pEntryHandle->url_search_pattern)
3524 {
3525 heap_free(pEntryHandle);
3526 return NULL;
3527 }
3528 }
3529 else
3530 pEntryHandle->url_search_pattern = NULL;
3531 pEntryHandle->container_idx = 0;
3532 pEntryHandle->hash_table_idx = 0;
3533 pEntryHandle->hash_entry_idx = 0;
3534
3535 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3536 {
3537 heap_free(pEntryHandle);
3538 return NULL;
3539 }
3540 return pEntryHandle;
3541}
3542
3544 HANDLE hEnumHandle,
3545 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3546 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3547 BOOL unicode)
3548{
3549 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3550 cache_container *pContainer;
3551
3552 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3553 {
3555 return FALSE;
3556 }
3557
3558 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3559 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3560 {
3562 entry_hash_table *pHashTableEntry;
3563 DWORD error;
3564
3566 if (error != ERROR_SUCCESS)
3567 {
3569 return FALSE;
3570 }
3571
3572 if (!(pHeader = cache_container_lock_index(pContainer)))
3573 return FALSE;
3574
3575 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3576 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3577 {
3578 const struct hash_entry *pHashEntry = NULL;
3579 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3580 pEntryHandle->hash_entry_idx++)
3581 {
3582 const entry_url *pUrlEntry;
3583 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3584
3585 if (pEntry->signature != URL_SIGNATURE)
3586 continue;
3587
3588 pUrlEntry = (const entry_url *)pEntry;
3589 TRACE("Found URL: %s\n",
3590 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3591 TRACE("Header info: %s\n",
3592 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3593 pUrlEntry->header_info_size));
3594
3596 pContainer,
3597 pHeader,
3598 lpNextCacheEntryInfo,
3599 lpdwNextCacheEntryInfoBufferSize,
3600 pUrlEntry,
3601 unicode);
3602 if (error != ERROR_SUCCESS)
3603 {
3606 return FALSE;
3607 }
3608 if(pUrlEntry->local_name_off)
3609 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3610
3611 /* increment the current index so that next time the function
3612 * is called the next entry is returned */
3613 pEntryHandle->hash_entry_idx++;
3615 return TRUE;
3616 }
3617 }
3618
3620 }
3621
3623 return FALSE;
3624}
3625
3626/***********************************************************************
3627 * FindNextUrlCacheEntryA (WININET.@)
3628 */
3630 HANDLE hEnumHandle,
3631 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3632 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3633{
3634 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3635
3636 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3637 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3638}
3639
3640/***********************************************************************
3641 * FindNextUrlCacheEntryW (WININET.@)
3642 */
3644 HANDLE hEnumHandle,
3645 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3646 LPDWORD lpdwNextCacheEntryInfoBufferSize
3647)
3648{
3649 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3650
3651 return urlcache_find_next_entry(hEnumHandle,
3652 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3653 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3654}
3655
3656/***********************************************************************
3657 * FindCloseUrlCache (WININET.@)
3658 */
3660{
3661 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3662
3663 TRACE("(%p)\n", hEnumHandle);
3664
3665 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3666 {
3668 return FALSE;
3669 }
3670
3671 pEntryHandle->magic = 0;
3672 heap_free(pEntryHandle->url_search_pattern);
3673 heap_free(pEntryHandle);
3674 return TRUE;
3675}
3676
3678 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3679{
3680 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3681 dwSearchCondition, lpGroupId, lpReserved);
3682 return NULL;
3683}
3684
3686 HANDLE hEnumHandle,
3687 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3688 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3689 LPVOID lpReserved,
3690 LPDWORD pcbReserved2,
3691 LPVOID lpReserved3
3692)
3693{
3694 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3695 lpReserved, pcbReserved2, lpReserved3);
3696 return FALSE;
3697}
3698
3700 HANDLE hEnumHandle,
3701 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3702 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3703 LPVOID lpReserved,
3704 LPDWORD pcbReserved2,
3705 LPVOID lpReserved3
3706)
3707{
3708 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3709 lpReserved, pcbReserved2, lpReserved3);
3710 return FALSE;
3711}
3712
3713BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3714{
3715 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3716 return FALSE;
3717}
3718
3719/***********************************************************************
3720 * CreateUrlCacheGroup (WININET.@)
3721 *
3722 */
3724{
3725 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3726 return FALSE;
3727}
3728
3729/***********************************************************************
3730 * DeleteUrlCacheGroup (WININET.@)
3731 *
3732 */
3734{
3735 FIXME("(0x%s, 0x%08x, %p) stub\n",
3736 wine_dbgstr_longlong(GroupId), dwFlags, lpReserved);
3737 return FALSE;
3738}
3739
3740/***********************************************************************
3741 * DeleteWpadCacheForNetworks (WININET.@)
3742 * Undocumented, added in IE8
3743 */
3745{
3746 FIXME("(%d) stub\n", unk1);
3747 return FALSE;
3748}
3749
3750/***********************************************************************
3751 * SetUrlCacheEntryGroupA (WININET.@)
3752 *
3753 */
3755 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3756 LPVOID lpReserved)
3757{
3758 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3759 debugstr_a(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3760 pbGroupAttributes, cbGroupAttributes, lpReserved);
3762 return FALSE;
3763}
3764
3765/***********************************************************************
3766 * SetUrlCacheEntryGroupW (WININET.@)
3767 *
3768 */
3770 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3771 LPVOID lpReserved)
3772{
3773 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3774 debugstr_w(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3775 pbGroupAttributes, cbGroupAttributes, lpReserved);
3777 return FALSE;
3778}
3779
3781{
3783
3785 {
3787 {
3788 case 0:
3790 if (container->default_entry_type == NORMAL_CACHE_ENTRY)
3791 return container;
3792 break;
3793
3795 if (container->default_entry_type == COOKIE_CACHE_ENTRY)
3796 return container;
3797 break;
3798
3800 if (container->default_entry_type == URLHISTORY_CACHE_ENTRY)
3801 return container;
3802 break;
3803
3804 default:
3805 FIXME("flags %08x not handled\n", flags);
3806 break;
3807 }
3808 }
3809
3810 return NULL;
3811}
3812
3813/***********************************************************************
3814 * GetUrlCacheConfigInfoW (WININET.@)
3815 */
3817{
3819 DWORD error;
3820
3821 FIXME("(%p, %p, %x): semi-stub\n", info, size, flags);
3822
3823 if (!info || !(container = find_container(flags)))
3824 {
3826 return FALSE;
3827 }
3828
3830 if (error != ERROR_SUCCESS)
3831 {
3833 return FALSE;
3834 }
3835
3836 info->dwContainer = 0;
3837 info->dwQuota = FILE_SIZE(MAX_BLOCK_NO) / 1024;
3838 info->dwReserved4 = 0;
3839 info->fPerUser = TRUE;
3840 info->dwSyncMode = 0;
3841 info->dwNumCachePaths = 1;
3842 info->dwNormalUsage = 0;
3843 info->dwExemptUsage = 0;
3844 info->u.s.dwCacheSize = container->file_size / 1024;
3845 lstrcpynW(info->u.s.CachePath, container->path, MAX_PATH);
3846
3848
3849 TRACE("CachePath %s\n", debugstr_w(info->u.s.CachePath));
3850
3851 return TRUE;
3852}
3853
3854/***********************************************************************
3855 * GetUrlCacheConfigInfoA (WININET.@)
3856 */
3858{
3860
3861 TRACE("(%p, %p, %x)\n", info, size, flags);
3862
3863 if (!info)
3864 {
3866 return FALSE;
3867 }
3868
3869 infoW.dwStructSize = sizeof(infoW);
3871 return FALSE;
3872
3873 info->dwContainer = infoW.dwContainer;
3874 info->dwQuota = infoW.dwQuota;
3875 info->dwReserved4 = infoW.dwReserved4;
3876 info->fPerUser = infoW.fPerUser;
3877 info->dwSyncMode = infoW.dwSyncMode;
3878 info->dwNumCachePaths = infoW.dwNumCachePaths;
3879 info->dwNormalUsage = infoW.dwNormalUsage;
3880 info->dwExemptUsage = infoW.dwExemptUsage;
3881 info->u.s.dwCacheSize = infoW.u.s.dwCacheSize;
3882 WideCharToMultiByte(CP_ACP, 0, infoW.u.s.CachePath, -1, info->u.s.CachePath, MAX_PATH, NULL, NULL);
3883
3884 return TRUE;
3885}
3886
3888 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3889 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3890{
3891 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3892 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3893 lpdwGroupInfo, lpReserved);
3894 return FALSE;
3895}
3896
3898 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3899 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3900{
3901 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3902 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3903 lpdwGroupInfo, lpReserved);
3904 return FALSE;
3905}
3906
3908 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3909{
3910 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3911 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3912 return TRUE;
3913}
3914
3916 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3917{
3918 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3919 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3920 return TRUE;
3921}
3922
3924{
3925 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3926 return TRUE;
3927}
3928
3930{
3931 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3932 return TRUE;
3933}
3934
3935/***********************************************************************
3936 * DeleteIE3Cache (WININET.@)
3937 *
3938 * Deletes the files used by the IE3 URL caching system.
3939 *
3940 * PARAMS
3941 * hWnd [I] A dummy window.
3942 * hInst [I] Instance of process calling the function.
3943 * lpszCmdLine [I] Options used by function.
3944 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3945 */
3947{
3948 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3949 return 0;
3950}
3951
3953 FILETIME *pftLastModified)
3954{
3955 BOOL ret;
3956 FILETIME now, expired;
3957
3958 *pftLastModified = pUrlEntry->modification_time;
3960