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