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