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