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