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