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