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