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