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