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