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