mpr: Remove unused variables.
[wine] / dlls / wininet / urlcache.c
1 /*
2  * Wininet - Url Cache functions
3  *
4  * Copyright 2001,2002 CodeWeavers
5  * Copyright 2003 Robert Shearman
6  *
7  * Eric Kohl
8  * Aric Stewart
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
36
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winuser.h"
40 #include "wininet.h"
41 #include "winineti.h"
42 #include "winerror.h"
43 #include "internet.h"
44 #include "winreg.h"
45 #include "shlwapi.h"
46 #include "shlobj.h"
47
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
52
53 #define ENTRY_START_OFFSET  0x4000
54 #define DIR_LENGTH          8
55 #define BLOCKSIZE           128
56 #define HASHTABLE_SIZE      448
57 #define HASHTABLE_BLOCKSIZE 7
58 #define HASHTABLE_FREE      3
59 #define ALLOCATION_TABLE_OFFSET 0x250
60 #define ALLOCATION_TABLE_SIZE   (0x1000 - ALLOCATION_TABLE_OFFSET)
61 #define HASHTABLE_NUM_ENTRIES   (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
62 #define NEWFILE_NUM_BLOCKS      0xd80
63 #define NEWFILE_SIZE            (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
64
65 #define DWORD_SIG(a,b,c,d)  (a | (b << 8) | (c << 16) | (d << 24))
66 #define URL_SIGNATURE   DWORD_SIG('U','R','L',' ')
67 #define REDR_SIGNATURE  DWORD_SIG('R','E','D','R')
68 #define LEAK_SIGNATURE  DWORD_SIG('L','E','A','K')
69 #define HASH_SIGNATURE  DWORD_SIG('H','A','S','H')
70
71 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
72
73 typedef struct _CACHEFILE_ENTRY
74 {
75 /*  union
76     {*/
77         DWORD dwSignature; /* e.g. "URL " */
78 /*      CHAR szSignature[4];
79     };*/
80     DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
81 } CACHEFILE_ENTRY;
82
83 typedef struct _URL_CACHEFILE_ENTRY
84 {
85     CACHEFILE_ENTRY CacheFileEntry;
86     FILETIME LastModifiedTime;
87     FILETIME LastAccessTime;
88     WORD wExpiredDate; /* expire date in dos format */
89     WORD wExpiredTime; /* expire time in dos format */
90     DWORD dwUnknown1; /* usually zero */
91     DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
92     DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
93     DWORD dwUnknown2; /* usually zero */
94     DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
95     DWORD dwUnknown3; /* usually 0x60 */
96     DWORD dwOffsetUrl; /* offset of start of url from start of entry */
97     BYTE CacheDir; /* index of cache directory this url is stored in */
98     BYTE Unknown4; /* usually zero */
99     WORD wUnknown5; /* usually 0x1010 */
100     DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
101     DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
102     DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
103     DWORD dwHeaderInfoSize;
104     DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
105     WORD wLastSyncDate; /* last sync date in dos format */
106     WORD wLastSyncTime; /* last sync time in dos format */
107     DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
108     DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109     WORD wUnknownDate; /* usually same as wLastSyncDate */
110     WORD wUnknownTime; /* usually same as wLastSyncTime */
111     DWORD dwUnknown7; /* usually zero */
112     DWORD dwUnknown8; /* usually zero */
113     /* packing to dword align start of next field */
114     /* CHAR szSourceUrlName[]; (url) */
115     /* packing to dword align start of next field */
116     /* CHAR szLocalFileName[]; (local file name excluding path) */
117     /* packing to dword align start of next field */
118     /* CHAR szHeaderInfo[]; (header info) */
119 } URL_CACHEFILE_ENTRY;
120
121 struct _HASH_ENTRY
122 {
123     DWORD dwHashKey;
124     DWORD dwOffsetEntry;
125 };
126
127 typedef struct _HASH_CACHEFILE_ENTRY
128 {
129     CACHEFILE_ENTRY CacheFileEntry;
130     DWORD dwAddressNext;
131     DWORD dwHashTableNumber;
132     struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
133 } HASH_CACHEFILE_ENTRY;
134
135 typedef struct _DIRECTORY_DATA
136 {
137     DWORD dwUnknown;
138     char filename[DIR_LENGTH];
139 } DIRECTORY_DATA;
140
141 typedef struct _URLCACHE_HEADER
142 {
143     char szSignature[28];
144     DWORD dwFileSize;
145     DWORD dwOffsetFirstHashTable;
146     DWORD dwIndexCapacityInBlocks;
147     DWORD dwBlocksInUse;
148     DWORD dwUnknown1;
149     DWORD dwCacheLimitLow; /* disk space limit for cache */
150     DWORD dwCacheLimitHigh; /* disk space limit for cache */
151     DWORD dwUnknown4; /* current disk space usage for cache */
152     DWORD dwUnknown5; /* current disk space usage for cache */
153     DWORD dwUnknown6; /* possibly a flag? */
154     DWORD dwUnknown7;
155     BYTE DirectoryCount; /* number of directory_data's */
156     BYTE Unknown8[3]; /* just padding? */
157     DIRECTORY_DATA directory_data[1]; /* first directory entry */
158 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
159 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
160
161 typedef struct _STREAM_HANDLE
162 {
163     HANDLE hFile;
164     CHAR lpszUrl[1];
165 } STREAM_HANDLE;
166
167 typedef struct _URLCACHECONTAINER
168 {
169     struct list entry; /* part of a list */
170     LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
171     LPWSTR path; /* path to url container directory */
172     HANDLE hMapping; /* handle of file mapping */
173     DWORD file_size; /* size of file when mapping was opened */
174     HANDLE hMutex; /* handle of mutex */
175 } URLCACHECONTAINER;
176
177
178 /* List of all containers available */
179 static struct list UrlContainers = LIST_INIT(UrlContainers);
180
181 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash);
182
183 /***********************************************************************
184  *           URLCache_PathToObjectName (Internal)
185  *
186  *  Converts a path to a name suitable for use as a Win32 object name.
187  * Replaces '\\' characters in-place with the specified character
188  * (usually '_' or '!')
189  *
190  * RETURNS
191  *    nothing
192  *
193  */
194 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
195 {
196     for (; *lpszPath; lpszPath++)
197     {
198         if (*lpszPath == '\\')
199             *lpszPath = replace;
200     }
201 }
202
203 /***********************************************************************
204  *           URLCacheContainer_OpenIndex (Internal)
205  *
206  *  Opens the index file and saves mapping handle in hCacheIndexMapping
207  *
208  * RETURNS
209  *    TRUE if succeeded
210  *    FALSE if failed
211  *
212  */
213 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
214 {
215     HANDLE hFile;
216     WCHAR wszFilePath[MAX_PATH];
217     DWORD dwFileSize;
218
219     static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
220     static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
221
222     if (pContainer->hMapping)
223         return TRUE;
224
225     strcpyW(wszFilePath, pContainer->path);
226     strcatW(wszFilePath, wszIndex);
227
228     hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
229     if (hFile == INVALID_HANDLE_VALUE)
230     {
231         /* Maybe the directory wasn't there? Try to create it */
232         if (CreateDirectoryW(pContainer->path, 0))
233             hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
234     }
235     if (hFile == INVALID_HANDLE_VALUE)
236     {
237         TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
238         return FALSE;
239     }
240
241     /* At this stage we need the mutex because we may be about to create the
242      * file.
243      */
244     WaitForSingleObject(pContainer->hMutex, INFINITE);
245
246     dwFileSize = GetFileSize(hFile, NULL);
247     if (dwFileSize == INVALID_FILE_SIZE)
248     {
249         ReleaseMutex(pContainer->hMutex);
250         return FALSE;
251     }
252
253     if (dwFileSize == 0)
254     {
255         static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
256         HKEY    key;
257         char    achZeroes[0x1000];
258         DWORD   dwOffset;
259         DWORD dwError = 0;
260
261         /* Write zeroes to the entire file so we can safely map it without
262          * fear of getting a SEGV because the disk is full.
263          */
264         memset(achZeroes, 0, sizeof(achZeroes));
265         for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
266         {
267             DWORD dwWrite = sizeof(achZeroes);
268             DWORD dwWritten;
269
270             if (NEWFILE_SIZE - dwOffset < dwWrite)
271                 dwWrite = NEWFILE_SIZE - dwOffset;
272             if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
273                 dwWritten != dwWrite)
274             {
275                 /* If we fail to write, we need to return the error that
276                  * cause the problem and also make sure the file is no
277                  * longer there, if possible.
278                  */
279                 dwError = GetLastError();
280
281                 break;
282             }
283         }
284
285         if (!dwError)
286         {
287             HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
288
289             if (hMapping)
290             {
291                 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
292
293                 if (pHeader)
294                 {
295                     WCHAR *pwchDir;
296                     WCHAR wszDirPath[MAX_PATH];
297                     FILETIME ft;
298                     int i, j;
299
300                     dwFileSize = NEWFILE_SIZE;
301                 
302                     /* First set some constants and defaults in the header */
303                     strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
304                     pHeader->dwFileSize = dwFileSize;
305                     pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
306                     /* 127MB - taken from default for Windows 2000 */
307                     pHeader->dwCacheLimitHigh = 0;
308                     pHeader->dwCacheLimitLow = 0x07ff5400;
309                     /* Copied from a Windows 2000 cache index */
310                     pHeader->DirectoryCount = 4;
311                 
312                     /* If the registry has a cache size set, use the registry value */
313                     if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
314                     {
315                         DWORD dw;
316                         DWORD len = sizeof(dw);
317                         DWORD keytype;
318                 
319                         if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
320                                              (BYTE *) &dw, &len) == ERROR_SUCCESS &&
321                             keytype == REG_DWORD)
322                         {
323                             pHeader->dwCacheLimitHigh = (dw >> 22);
324                             pHeader->dwCacheLimitLow = dw << 10;
325                         }
326                         RegCloseKey(key);
327                     }
328                 
329                     URLCache_CreateHashTable(pHeader, NULL);
330
331                     /* Last step - create the directories */
332         
333                     strcpyW(wszDirPath, pContainer->path);
334                     pwchDir = wszDirPath + strlenW(wszDirPath);
335                     pwchDir[8] = 0;
336         
337                     GetSystemTimeAsFileTime(&ft);
338         
339                     for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
340                     {
341                         /* The following values were copied from a Windows index.
342                          * I don't know what the values are supposed to mean but
343                          * have made them the same in the hope that this will
344                          * be better for compatibility
345                          */
346                         pHeader->directory_data[i].dwUnknown = (i > 1) ? 0xfe : 0xff;
347                         for (j = 0;; ++j)
348                         {
349                             int k;
350                             ULONGLONG n = ft.dwHighDateTime;
351         
352                             /* Generate a file name to attempt to create.
353                              * This algorithm will create what will appear
354                              * to be random and unrelated directory names
355                              * of up to 9 characters in length.
356                              */
357                             n <<= 32;
358                             n += ft.dwLowDateTime;
359                             n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
360         
361                             for (k = 0; k < 8; ++k)
362                             {
363                                 int r = (n % 36);
364         
365                                 /* Dividing by a prime greater than 36 helps
366                                  * with the appearance of randomness
367                                  */
368                                 n /= 37;
369         
370                                 if (r < 10)
371                                     pwchDir[k] = '0' + r;
372                                 else
373                                     pwchDir[k] = 'A' + (r - 10);
374                             }
375         
376                             if (CreateDirectoryW(wszDirPath, 0))
377                             {
378                                 /* The following is OK because we generated an
379                                  * 8 character directory name made from characters
380                                  * [A-Z0-9], which are equivalent for all code
381                                  * pages and for UTF-16
382                                  */
383                                 for (k = 0; k < 8; ++k)
384                                     pHeader->directory_data[i].filename[k] = pwchDir[k];
385                                 break;
386                             }
387                             else if (j >= 255)
388                             {
389                                 /* Give up. The most likely cause of this
390                                  * is a full disk, but whatever the cause
391                                  * is, it should be more than apparent that
392                                  * we won't succeed.
393                                  */
394                                 dwError = GetLastError();
395                                 break;
396                             }
397                         }
398                     }
399                 
400                     UnmapViewOfFile(pHeader);
401                 }
402                 else
403                 {
404                     dwError = GetLastError();
405                 }
406                 CloseHandle(hMapping);
407             }
408             else
409             {
410                 dwError = GetLastError();
411             }
412         }
413
414         if (dwError)
415         {
416             CloseHandle(hFile);
417             DeleteFileW(wszFilePath);
418             ReleaseMutex(pContainer->hMutex);
419             SetLastError(dwError);
420             return FALSE;
421         }
422
423     }
424
425     ReleaseMutex(pContainer->hMutex);
426
427     wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
428     URLCache_PathToObjectName(wszFilePath, '_');
429     pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
430     if (!pContainer->hMapping)
431         pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
432     CloseHandle(hFile);
433     if (!pContainer->hMapping)
434     {
435         ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
436         return FALSE;
437     }
438
439     return TRUE;
440 }
441
442 /***********************************************************************
443  *           URLCacheContainer_CloseIndex (Internal)
444  *
445  *  Closes the index
446  *
447  * RETURNS
448  *    nothing
449  *
450  */
451 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
452 {
453     CloseHandle(pContainer->hMapping);
454     pContainer->hMapping = NULL;
455 }
456
457 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
458 {
459     URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
460     int path_len = strlenW(path);
461     int cache_prefix_len = strlenW(cache_prefix);
462
463     if (!pContainer)
464     {
465         return FALSE;
466     }
467
468     pContainer->hMapping = NULL;
469     pContainer->file_size = 0;
470
471     pContainer->path = HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
472     if (!pContainer->path)
473     {
474         HeapFree(GetProcessHeap(), 0, pContainer);
475         return FALSE;
476     }
477
478     memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
479
480     pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
481     if (!pContainer->cache_prefix)
482     {
483         HeapFree(GetProcessHeap(), 0, pContainer->path);
484         HeapFree(GetProcessHeap(), 0, pContainer);
485         return FALSE;
486     }
487
488     memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
489
490     CharLowerW(mutex_name);
491     URLCache_PathToObjectName(mutex_name, '!');
492
493     if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
494     {
495         ERR("couldn't create mutex (error is %d)\n", GetLastError());
496         HeapFree(GetProcessHeap(), 0, pContainer->path);
497         HeapFree(GetProcessHeap(), 0, pContainer);
498         return FALSE;
499     }
500
501     list_add_head(&UrlContainers, &pContainer->entry);
502
503     return TRUE;
504 }
505
506 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
507 {
508     list_remove(&pContainer->entry);
509
510     URLCacheContainer_CloseIndex(pContainer);
511     CloseHandle(pContainer->hMutex);
512     HeapFree(GetProcessHeap(), 0, pContainer->path);
513     HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
514     HeapFree(GetProcessHeap(), 0, pContainer);
515 }
516
517 void URLCacheContainers_CreateDefaults(void)
518 {
519     static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
520     static const WCHAR UrlPrefix[] = {0};
521     static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
522     static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
523     static const WCHAR CookieSuffix[] = {0};
524     static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
525     static const struct
526     {
527         int nFolder; /* CSIDL_* constant */
528         const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
529         const WCHAR * cache_prefix; /* prefix used to reference the container */
530     } DefaultContainerData[] = 
531     {
532         { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
533         { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
534         { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
535     };
536     DWORD i;
537
538     for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
539     {
540         WCHAR wszCachePath[MAX_PATH];
541         WCHAR wszMutexName[MAX_PATH];
542         int path_len, suffix_len;
543
544         if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
545         {
546             ERR("Couldn't get path for default container %u\n", i);
547             continue;
548         }
549         path_len = strlenW(wszCachePath);
550         suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
551
552         if (path_len + suffix_len + 2 > MAX_PATH)
553         {
554             ERR("Path too long\n");
555             continue;
556         }
557
558         wszCachePath[path_len] = '\\';
559         wszCachePath[path_len+1] = 0;
560
561         strcpyW(wszMutexName, wszCachePath);
562         
563         if (suffix_len)
564         {
565             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
566             wszCachePath[path_len + suffix_len + 1] = '\\';
567             wszCachePath[path_len + suffix_len + 2] = '\0';
568         }
569
570         URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
571     }
572 }
573
574 void URLCacheContainers_DeleteAll(void)
575 {
576     while(!list_empty(&UrlContainers))
577         URLCacheContainer_DeleteContainer(
578             LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
579         );
580 }
581
582 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
583 {
584     URLCACHECONTAINER * pContainer;
585
586     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
587
588     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
589     {
590         int prefix_len = strlenW(pContainer->cache_prefix);
591         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
592         {
593             TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
594             *ppContainer = pContainer;
595             return TRUE;
596         }
597     }
598     ERR("no container found\n");
599     SetLastError(ERROR_FILE_NOT_FOUND);
600     return FALSE;
601 }
602
603 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
604 {
605     BOOL ret;
606     LPWSTR lpwszUrl;
607     int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
608     if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
609     {
610         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
611         ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
612         HeapFree(GetProcessHeap(), 0, lpwszUrl);
613         return ret;
614     }
615     return FALSE;
616 }
617
618 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
619 {
620     DWORD i = 0;
621     URLCACHECONTAINER * pContainer;
622
623     TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
624
625     /* non-NULL search pattern only returns one container ever */
626     if (lpwszSearchPattern && dwIndex > 0)
627         return FALSE;
628
629     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
630     {
631         if (lpwszSearchPattern)
632         {
633             if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
634             {
635                 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
636                 *ppContainer = pContainer;
637                 return TRUE;
638             }
639         }
640         else
641         {
642             if (i == dwIndex)
643             {
644                 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
645                 *ppContainer = pContainer;
646                 return TRUE;
647             }
648         }
649         i++;
650     }
651     return FALSE;
652 }
653
654 /***********************************************************************
655  *           URLCacheContainer_LockIndex (Internal)
656  *
657  */
658 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
659 {
660     BYTE index;
661     LPVOID pIndexData;
662     URLCACHE_HEADER * pHeader;
663
664     /* acquire mutex */
665     WaitForSingleObject(pContainer->hMutex, INFINITE);
666
667     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
668
669     if (!pIndexData)
670     {
671         ReleaseMutex(pContainer->hMutex);
672         ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
673         return FALSE;
674     }
675     pHeader = (URLCACHE_HEADER *)pIndexData;
676
677     /* file has grown - we need to remap to prevent us getting
678      * access violations when we try and access beyond the end
679      * of the memory mapped file */
680     if (pHeader->dwFileSize != pContainer->file_size)
681     {
682         URLCacheContainer_CloseIndex(pContainer);
683         if (!URLCacheContainer_OpenIndex(pContainer))
684         {
685             ReleaseMutex(pContainer->hMutex);
686             return FALSE;
687         }
688         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
689
690         if (!pIndexData)
691         {
692             ReleaseMutex(pContainer->hMutex);
693             ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
694             return FALSE;
695         }
696         pHeader = (URLCACHE_HEADER *)pIndexData;
697     }
698
699     TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
700
701     for (index = 0; index < pHeader->DirectoryCount; index++)
702     {
703         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
704     }
705     
706     return pHeader;
707 }
708
709 /***********************************************************************
710  *           URLCacheContainer_UnlockIndex (Internal)
711  *
712  */
713 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
714 {
715     /* release mutex */
716     ReleaseMutex(pContainer->hMutex);
717     return UnmapViewOfFile(pHeader);
718 }
719
720
721 #ifndef CHAR_BIT
722 #define CHAR_BIT    (8 * sizeof(CHAR))
723 #endif
724
725 /***********************************************************************
726  *           URLCache_Allocation_BlockIsFree (Internal)
727  *
728  *  Is the specified block number free?
729  *
730  * RETURNS
731  *    zero if free
732  *    non-zero otherwise
733  *
734  */
735 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
736 {
737     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
738     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
739 }
740
741 /***********************************************************************
742  *           URLCache_Allocation_BlockFree (Internal)
743  *
744  *  Marks the specified block as free
745  *
746  * RETURNS
747  *    nothing
748  *
749  */
750 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
751 {
752     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
753     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
754 }
755
756 /***********************************************************************
757  *           URLCache_Allocation_BlockAllocate (Internal)
758  *
759  *  Marks the specified block as allocated
760  *
761  * RETURNS
762  *    nothing
763  *
764  */
765 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
766 {
767     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
768     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
769 }
770
771 /***********************************************************************
772  *           URLCache_FindFirstFreeEntry (Internal)
773  *
774  *  Finds and allocates the first block of free space big enough and
775  * sets ppEntry to point to it.
776  *
777  * RETURNS
778  *    TRUE if it had enough space
779  *    FALSE if it couldn't find enough space
780  *
781  */
782 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
783 {
784     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
785     DWORD dwBlockNumber;
786     DWORD dwFreeCounter;
787     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
788     {
789         for (dwFreeCounter = 0; 
790             dwFreeCounter < dwBlocksNeeded &&
791               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
792               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
793             dwFreeCounter++)
794                 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
795
796         if (dwFreeCounter == dwBlocksNeeded)
797         {
798             DWORD index;
799             TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
800             for (index = 0; index < dwBlocksNeeded; index++)
801                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
802             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
803             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
804             return TRUE;
805         }
806     }
807     FIXME("Grow file\n");
808     return FALSE;
809 }
810
811 /***********************************************************************
812  *           URLCache_DeleteEntry (Internal)
813  *
814  *  Deletes the specified entry and frees the space allocated to it
815  *
816  * RETURNS
817  *    TRUE if it succeeded
818  *    FALSE if it failed
819  *
820  */
821 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
822 {
823     DWORD dwStartBlock;
824     DWORD dwBlock;
825     BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
826
827     /* update allocation table */
828     dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
829     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
830         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
831
832     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
833     return TRUE;
834 }
835
836 /***********************************************************************
837  *           URLCache_LocalFileNameToPathW (Internal)
838  *
839  *  Copies the full path to the specified buffer given the local file
840  * name and the index of the directory it is in. Always sets value in
841  * lpBufferSize to the required buffer size (in bytes).
842  *
843  * RETURNS
844  *    TRUE if the buffer was big enough
845  *    FALSE if the buffer was too small
846  *
847  */
848 static BOOL URLCache_LocalFileNameToPathW(
849     const URLCACHECONTAINER * pContainer,
850     LPCURLCACHE_HEADER pHeader,
851     LPCSTR szLocalFileName,
852     BYTE Directory,
853     LPWSTR wszPath,
854     LPLONG lpBufferSize)
855 {
856     LONG nRequired;
857     int path_len = strlenW(pContainer->path);
858     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
859     if (Directory >= pHeader->DirectoryCount)
860     {
861         *lpBufferSize = 0;
862         return FALSE;
863     }
864
865     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
866     if (nRequired < *lpBufferSize)
867     {
868         int dir_len;
869
870         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
871         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
872         wszPath[dir_len + path_len] = '\\';
873         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
874         *lpBufferSize = nRequired;
875         return TRUE;
876     }
877     *lpBufferSize = nRequired;
878     return FALSE;
879 }
880
881 /***********************************************************************
882  *           URLCache_LocalFileNameToPathA (Internal)
883  *
884  *  Copies the full path to the specified buffer given the local file
885  * name and the index of the directory it is in. Always sets value in
886  * lpBufferSize to the required buffer size.
887  *
888  * RETURNS
889  *    TRUE if the buffer was big enough
890  *    FALSE if the buffer was too small
891  *
892  */
893 static BOOL URLCache_LocalFileNameToPathA(
894     const URLCACHECONTAINER * pContainer,
895     LPCURLCACHE_HEADER pHeader,
896     LPCSTR szLocalFileName,
897     BYTE Directory,
898     LPSTR szPath,
899     LPLONG lpBufferSize)
900 {
901     LONG nRequired;
902     int path_len, file_name_len, dir_len;
903
904     if (Directory >= pHeader->DirectoryCount)
905     {
906         *lpBufferSize = 0;
907         return FALSE;
908     }
909
910     path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
911     file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
912     dir_len = DIR_LENGTH;
913
914     nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
915     if (nRequired < *lpBufferSize)
916     {
917         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
918         memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
919         szPath[path_len + dir_len] = '\\';
920         memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
921         *lpBufferSize = nRequired;
922         return TRUE;
923     }
924     *lpBufferSize = nRequired;
925     return FALSE;
926 }
927
928 /***********************************************************************
929  *           URLCache_CopyEntry (Internal)
930  *
931  *  Copies an entry from the cache index file to the Win32 structure
932  *
933  * RETURNS
934  *    TRUE if the buffer was big enough
935  *    FALSE if the buffer was too small
936  *
937  */
938 static BOOL URLCache_CopyEntry(
939     URLCACHECONTAINER * pContainer,
940     LPCURLCACHE_HEADER pHeader, 
941     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
942     LPDWORD lpdwBufferSize, 
943     const URL_CACHEFILE_ENTRY * pUrlEntry,
944     BOOL bUnicode)
945 {
946     int lenUrl;
947     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
948
949     if (*lpdwBufferSize >= dwRequiredSize)
950     {
951         lpCacheEntryInfo->lpHeaderInfo = NULL;
952         lpCacheEntryInfo->lpszFileExtension = NULL;
953         lpCacheEntryInfo->lpszLocalFileName = NULL;
954         lpCacheEntryInfo->lpszSourceUrlName = NULL;
955         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
956         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
957         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
958         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
959         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
960         lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
961         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
962         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
963         DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
964         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
965         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
966         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
967         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
968         DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
969     }
970
971     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
972         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
973     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
974     if (bUnicode)
975         lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
976     else
977         lenUrl = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
978     dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
979
980     /* FIXME: is source url optional? */
981     if (*lpdwBufferSize >= dwRequiredSize)
982     {
983         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
984         if (bUnicode)
985             MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
986         else
987             memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, (lenUrl + 1) * sizeof(CHAR));
988     }
989
990     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
991         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
992     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
993
994     if (pUrlEntry->dwOffsetLocalName)
995     {
996         LONG nLocalFilePathSize;
997         LPSTR lpszLocalFileName;
998         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
999         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1000         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1001             URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
1002         {
1003             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1004         }
1005         dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1006
1007         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1008             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1009         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1010     }
1011     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1012
1013     if (*lpdwBufferSize >= dwRequiredSize)
1014     {
1015         lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1016         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1017         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1018     }
1019     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1020         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1021     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1022
1023     if (pUrlEntry->dwOffsetFileExtension)
1024     {
1025         int lenExtension;
1026
1027         if (bUnicode)
1028             lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1029         else
1030             lenExtension = strlen((LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1031         dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1032
1033         if (*lpdwBufferSize >= dwRequiredSize)
1034         {
1035             lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1036             if (bUnicode)
1037                 MultiByteToWideChar(CP_ACP, 0, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1038             else
1039                 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1040         }
1041
1042         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1043             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1044         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1045     }
1046
1047     if (dwRequiredSize > *lpdwBufferSize)
1048     {
1049         *lpdwBufferSize = dwRequiredSize;
1050         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1051         return FALSE;
1052     }
1053     *lpdwBufferSize = dwRequiredSize;
1054     return TRUE;
1055 }
1056
1057
1058 /***********************************************************************
1059  *           URLCache_SetEntryInfo (Internal)
1060  *
1061  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1062  * according to the flags set by dwFieldControl.
1063  *
1064  * RETURNS
1065  *    TRUE if the buffer was big enough
1066  *    FALSE if the buffer was too small
1067  *
1068  */
1069 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1070 {
1071     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1072         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1073     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1074         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1075     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1076         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1077     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1078         FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
1079     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1080         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1081     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1082         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1083     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1084         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1085     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1086         FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1087
1088     return TRUE;
1089 }
1090
1091 /***********************************************************************
1092  *           URLCache_HashKey (Internal)
1093  *
1094  *  Returns the hash key for a given string
1095  *
1096  * RETURNS
1097  *    hash key for the string
1098  *
1099  */
1100 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1101 {
1102     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1103      * but the algorithm and result are not the same!
1104      */
1105     static const unsigned char lookupTable[256] = 
1106     {
1107         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1108         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1109         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1110         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1111         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1112         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1113         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1114         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1115         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1116         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1117         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1118         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1119         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1120         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1121         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1122         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1123         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1124         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1125         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1126         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1127         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1128         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1129         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1130         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1131         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1132         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1133         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1134         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1135         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1136         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1137         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1138         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1139     };
1140     BYTE key[4];
1141     DWORD i;
1142     int subscript[sizeof(key) / sizeof(key[0])];
1143
1144     subscript[0] = *lpszKey;
1145     subscript[1] = (char)(*lpszKey + 1);
1146     subscript[2] = (char)(*lpszKey + 2);
1147     subscript[3] = (char)(*lpszKey + 3);
1148
1149     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1150         key[i] = lookupTable[i];
1151
1152     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1153     {
1154         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1155             key[i] = lookupTable[*lpszKey ^ key[i]];
1156     }
1157
1158     return *(DWORD *)key;
1159 }
1160
1161 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1162 {
1163     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1164 }
1165
1166 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1167 {
1168     /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1169     return ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) &&
1170            ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1171 }
1172
1173 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1174 {
1175     /* structure of hash table:
1176      *  448 entries divided into 64 blocks
1177      *  each block therefore contains a chain of 7 key/offset pairs
1178      * how position in table is calculated:
1179      *  1. the url is hashed in helper function
1180      *  2. the key % 64 * 8 is the offset
1181      *  3. the key in the hash table is the hash key aligned to 64
1182      *
1183      * note:
1184      *  there can be multiple hash tables in the file and the offset to
1185      *  the next one is stored in the header of the hash table
1186      */
1187     DWORD key = URLCache_HashKey(lpszUrl);
1188     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1189     HASH_CACHEFILE_ENTRY * pHashEntry;
1190     DWORD dwHashTableNumber = 0;
1191
1192     key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1193
1194     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1195          URLCache_IsHashEntryValid(pHeader, pHashEntry);
1196          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1197     {
1198         int i;
1199         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1200         {
1201             ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1202             continue;
1203         }
1204         /* make sure that it is in fact a hash entry */
1205         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1206         {
1207             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1208             continue;
1209         }
1210
1211         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1212         {
1213             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1214             if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1215             {
1216                 /* FIXME: we should make sure that this is the right element
1217                  * before returning and claiming that it is. We can do this
1218                  * by doing a simple compare between the URL we were given
1219                  * and the URL stored in the entry. However, this assumes
1220                  * we know the format of all the entries stored in the
1221                  * hash table */
1222                 *ppHashEntry = pHashElement;
1223                 return TRUE;
1224             }
1225         }
1226     }
1227     return FALSE;
1228 }
1229
1230 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1231 {
1232     LPSTR urlA;
1233     int url_len;
1234     BOOL ret;
1235
1236     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1237     urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1238     if (!urlA)
1239     {
1240         SetLastError(ERROR_OUTOFMEMORY);
1241         return FALSE;
1242     }
1243     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, urlA, url_len, NULL, NULL);
1244     ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1245     HeapFree(GetProcessHeap(), 0, urlA);
1246     return ret;
1247 }
1248
1249 /***********************************************************************
1250  *           URLCache_HashEntrySetUse (Internal)
1251  *
1252  *  Searches all the hash tables in the index for the given URL and
1253  * sets the use count (stored or'ed with key)
1254  *
1255  * RETURNS
1256  *    TRUE if the entry was found
1257  *    FALSE if the entry could not be found
1258  *
1259  */
1260 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1261 {
1262     pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1263     return TRUE;
1264 }
1265
1266 /***********************************************************************
1267  *           URLCache_DeleteEntryFromHash (Internal)
1268  *
1269  *  Searches all the hash tables in the index for the given URL and
1270  * then if found deletes the entry.
1271  *
1272  * RETURNS
1273  *    TRUE if the entry was found
1274  *    FALSE if the entry could not be found
1275  *
1276  */
1277 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1278 {
1279     pHashEntry->dwHashKey = HASHTABLE_FREE;
1280     pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1281     return TRUE;
1282 }
1283
1284 /***********************************************************************
1285  *           URLCache_AddEntryToHash (Internal)
1286  *
1287  *  Searches all the hash tables for a free slot based on the offset
1288  * generated from the hash key. If a free slot is found, the offset and
1289  * key are entered into the hash table.
1290  *
1291  * RETURNS
1292  *    TRUE if the entry was added
1293  *    FALSE if the entry could not be added
1294  *
1295  */
1296 static BOOL URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1297 {
1298     /* see URLCache_FindEntryInHash for structure of hash tables */
1299
1300     DWORD key = URLCache_HashKey(lpszUrl);
1301     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1302     HASH_CACHEFILE_ENTRY * pHashEntry;
1303     DWORD dwHashTableNumber = 0;
1304
1305     key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1306
1307     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1308          URLCache_IsHashEntryValid(pHeader, pHashEntry);
1309          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1310     {
1311         int i;
1312         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1313         {
1314             ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1315             break;
1316         }
1317         /* make sure that it is in fact a hash entry */
1318         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1319         {
1320             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1321             break;
1322         }
1323
1324         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1325         {
1326             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1327             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1328             {
1329                 pHashElement->dwHashKey = key;
1330                 pHashElement->dwOffsetEntry = dwOffsetEntry;
1331                 return TRUE;
1332             }
1333         }
1334     }
1335     pHashEntry = URLCache_CreateHashTable(pHeader, pHashEntry);
1336     if (!pHashEntry)
1337         return FALSE;
1338
1339     pHashEntry->HashTable[offset].dwHashKey = key;
1340     pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1341     return TRUE;
1342 }
1343
1344 /***********************************************************************
1345  *           URLCache_CreateHashTable (Internal)
1346  *
1347  *  Creates a new hash table in free space and adds it to the chain of existing
1348  * hash tables.
1349  *
1350  * RETURNS
1351  *    TRUE if the hash table was created
1352  *    FALSE if the hash table could not be created
1353  *
1354  */
1355 static HASH_CACHEFILE_ENTRY *URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash)
1356 {
1357     HASH_CACHEFILE_ENTRY *pHash;
1358     DWORD dwOffset;
1359     int i;
1360
1361     if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)&pHash))
1362     {
1363         FIXME("no free space for hash table\n");
1364         SetLastError(ERROR_DISK_FULL);
1365         return NULL;
1366     }
1367
1368     dwOffset = (BYTE *)pHash - (BYTE *)pHeader;
1369
1370     if (pPrevHash)
1371         pPrevHash->dwAddressNext = dwOffset;
1372     else
1373         pHeader->dwOffsetFirstHashTable = dwOffset;
1374     pHash->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1375     pHash->CacheFileEntry.dwBlocksUsed = 0x20;
1376     pHash->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1377     for (i = 0; i < HASHTABLE_SIZE; i++)
1378     {
1379         pHash->HashTable[i].dwOffsetEntry = 0;
1380         pHash->HashTable[i].dwHashKey = HASHTABLE_FREE;
1381     }
1382     return pHash;
1383 }
1384
1385 /***********************************************************************
1386  *           URLCache_EnumHashTables (Internal)
1387  *
1388  *  Enumerates the hash tables in a container.
1389  *
1390  * RETURNS
1391  *    TRUE if an entry was found
1392  *    FALSE if there are no more tables to enumerate.
1393  *
1394  */
1395 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1396 {
1397     for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1398          URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1399          *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1400     {
1401         TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1402         if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1403             continue;
1404         /* make sure that it is in fact a hash entry */
1405         if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1406         {
1407             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1408             (*pdwHashTableNumber)++;
1409             continue;
1410         }
1411
1412         TRACE("hash table number %d found\n", *pdwHashTableNumber);
1413         return TRUE;
1414     }
1415     return FALSE;
1416 }
1417
1418 /***********************************************************************
1419  *           URLCache_EnumHashTableEntries (Internal)
1420  *
1421  *  Enumerates entries in a hash table and returns the next non-free entry.
1422  *
1423  * RETURNS
1424  *    TRUE if an entry was found
1425  *    FALSE if the hash table is empty or there are no more entries to
1426  *     enumerate.
1427  *
1428  */
1429 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1430                                           DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1431 {
1432     for (; *index < HASHTABLE_SIZE ; (*index)++)
1433     {
1434         if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1435             continue;
1436
1437         *ppHashEntry = &pHashEntry->HashTable[*index];
1438         TRACE("entry found %d\n", *index);
1439         return TRUE;
1440     }
1441     TRACE("no more entries (%d)\n", *index);
1442     return FALSE;
1443 }
1444
1445 /***********************************************************************
1446  *           GetUrlCacheEntryInfoExA (WININET.@)
1447  *
1448  */
1449 BOOL WINAPI GetUrlCacheEntryInfoExA(
1450     LPCSTR lpszUrl,
1451     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1452     LPDWORD lpdwCacheEntryInfoBufSize,
1453     LPSTR lpszReserved,
1454     LPDWORD lpdwReserved,
1455     LPVOID lpReserved,
1456     DWORD dwFlags)
1457 {
1458     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1459         debugstr_a(lpszUrl), 
1460         lpCacheEntryInfo,
1461         lpdwCacheEntryInfoBufSize,
1462         lpszReserved,
1463         lpdwReserved,
1464         lpReserved,
1465         dwFlags);
1466
1467     if ((lpszReserved != NULL) ||
1468         (lpdwReserved != NULL) ||
1469         (lpReserved != NULL))
1470     {
1471         ERR("Reserved value was not 0\n");
1472         SetLastError(ERROR_INVALID_PARAMETER);
1473         return FALSE;
1474     }
1475     if (dwFlags != 0)
1476         FIXME("Undocumented flag(s): %x\n", dwFlags);
1477     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1478 }
1479
1480 /***********************************************************************
1481  *           GetUrlCacheEntryInfoA (WININET.@)
1482  *
1483  */
1484 BOOL WINAPI GetUrlCacheEntryInfoA(
1485     IN LPCSTR lpszUrlName,
1486     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1487     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1488 )
1489 {
1490     LPURLCACHE_HEADER pHeader;
1491     struct _HASH_ENTRY * pHashEntry;
1492     const CACHEFILE_ENTRY * pEntry;
1493     const URL_CACHEFILE_ENTRY * pUrlEntry;
1494     URLCACHECONTAINER * pContainer;
1495
1496     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1497
1498     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1499         return FALSE;
1500
1501     if (!URLCacheContainer_OpenIndex(pContainer))
1502         return FALSE;
1503
1504     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1505         return FALSE;
1506
1507     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1508     {
1509         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1510         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1511         SetLastError(ERROR_FILE_NOT_FOUND);
1512         return FALSE;
1513     }
1514
1515     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1516     if (pEntry->dwSignature != URL_SIGNATURE)
1517     {
1518         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1519         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1520         SetLastError(ERROR_FILE_NOT_FOUND);
1521         return FALSE;
1522     }
1523
1524     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1525     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1526     if (pUrlEntry->dwOffsetHeaderInfo)
1527         TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1528
1529     if (!URLCache_CopyEntry(
1530         pContainer,
1531         pHeader,
1532         lpCacheEntryInfo,
1533         lpdwCacheEntryInfoBufferSize,
1534         pUrlEntry,
1535         FALSE /* ANSI */))
1536     {
1537         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1538         return FALSE;
1539     }
1540     TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1541
1542     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1543
1544     return TRUE;
1545 }
1546
1547 /***********************************************************************
1548  *           GetUrlCacheEntryInfoW (WININET.@)
1549  *
1550  */
1551 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1552   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1553   LPDWORD lpdwCacheEntryInfoBufferSize)
1554 {
1555     LPURLCACHE_HEADER pHeader;
1556     struct _HASH_ENTRY * pHashEntry;
1557     const CACHEFILE_ENTRY * pEntry;
1558     const URL_CACHEFILE_ENTRY * pUrlEntry;
1559     URLCACHECONTAINER * pContainer;
1560
1561     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1562
1563     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1564         return FALSE;
1565
1566     if (!URLCacheContainer_OpenIndex(pContainer))
1567         return FALSE;
1568
1569     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1570         return FALSE;
1571
1572     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1573     {
1574         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1575         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1576         SetLastError(ERROR_FILE_NOT_FOUND);
1577         return FALSE;
1578     }
1579
1580     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1581     if (pEntry->dwSignature != URL_SIGNATURE)
1582     {
1583         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1584         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1585         SetLastError(ERROR_FILE_NOT_FOUND);
1586         return FALSE;
1587     }
1588
1589     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1590     TRACE("Found URL: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1591     TRACE("Header info: %s\n", debugstr_a((LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1592
1593     if (!URLCache_CopyEntry(
1594         pContainer,
1595         pHeader,
1596         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1597         lpdwCacheEntryInfoBufferSize,
1598         pUrlEntry,
1599         TRUE /* UNICODE */))
1600     {
1601         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1602         return FALSE;
1603     }
1604     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1605
1606     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1607
1608     return TRUE;
1609 }
1610
1611 /***********************************************************************
1612  *           GetUrlCacheEntryInfoExW (WININET.@)
1613  *
1614  */
1615 BOOL WINAPI GetUrlCacheEntryInfoExW(
1616     LPCWSTR lpszUrl,
1617     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1618     LPDWORD lpdwCacheEntryInfoBufSize,
1619     LPWSTR lpszReserved,
1620     LPDWORD lpdwReserved,
1621     LPVOID lpReserved,
1622     DWORD dwFlags)
1623 {
1624     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1625         debugstr_w(lpszUrl), 
1626         lpCacheEntryInfo,
1627         lpdwCacheEntryInfoBufSize,
1628         lpszReserved,
1629         lpdwReserved,
1630         lpReserved,
1631         dwFlags);
1632
1633     if ((lpszReserved != NULL) ||
1634         (lpdwReserved != NULL) ||
1635         (lpReserved != NULL))
1636     {
1637         ERR("Reserved value was not 0\n");
1638         SetLastError(ERROR_INVALID_PARAMETER);
1639         return FALSE;
1640     }
1641     if (dwFlags != 0)
1642         FIXME("Undocumented flag(s): %x\n", dwFlags);
1643     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1644 }
1645
1646 /***********************************************************************
1647  *           SetUrlCacheEntryInfoA (WININET.@)
1648  */
1649 BOOL WINAPI SetUrlCacheEntryInfoA(
1650     LPCSTR lpszUrlName,
1651     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1652     DWORD dwFieldControl)
1653 {
1654     LPURLCACHE_HEADER pHeader;
1655     struct _HASH_ENTRY * pHashEntry;
1656     CACHEFILE_ENTRY * pEntry;
1657     URLCACHECONTAINER * pContainer;
1658
1659     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1660
1661     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1662         return FALSE;
1663
1664     if (!URLCacheContainer_OpenIndex(pContainer))
1665         return FALSE;
1666
1667     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1668         return FALSE;
1669
1670     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1671     {
1672         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1673         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1674         SetLastError(ERROR_FILE_NOT_FOUND);
1675         return FALSE;
1676     }
1677
1678     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1679     if (pEntry->dwSignature != URL_SIGNATURE)
1680     {
1681         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1682         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1683         SetLastError(ERROR_FILE_NOT_FOUND);
1684         return FALSE;
1685     }
1686
1687     URLCache_SetEntryInfo(
1688         (URL_CACHEFILE_ENTRY *)pEntry,
1689         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1690         dwFieldControl);
1691
1692     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1693
1694     return TRUE;
1695 }
1696
1697 /***********************************************************************
1698  *           SetUrlCacheEntryInfoW (WININET.@)
1699  */
1700 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1701 {
1702     LPURLCACHE_HEADER pHeader;
1703     struct _HASH_ENTRY * pHashEntry;
1704     CACHEFILE_ENTRY * pEntry;
1705     URLCACHECONTAINER * pContainer;
1706
1707     TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1708
1709     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1710         return FALSE;
1711
1712     if (!URLCacheContainer_OpenIndex(pContainer))
1713         return FALSE;
1714
1715     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1716         return FALSE;
1717
1718     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1719     {
1720         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1721         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1722         SetLastError(ERROR_FILE_NOT_FOUND);
1723         return FALSE;
1724     }
1725
1726     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1727     if (pEntry->dwSignature != URL_SIGNATURE)
1728     {
1729         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1730         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1731         SetLastError(ERROR_FILE_NOT_FOUND);
1732         return FALSE;
1733     }
1734
1735     URLCache_SetEntryInfo(
1736         (URL_CACHEFILE_ENTRY *)pEntry,
1737         lpCacheEntryInfo,
1738         dwFieldControl);
1739
1740     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1741
1742     return TRUE;
1743 }
1744
1745 /***********************************************************************
1746  *           RetrieveUrlCacheEntryFileA (WININET.@)
1747  *
1748  */
1749 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1750     IN LPCSTR lpszUrlName,
1751     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1752     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1753     IN DWORD dwReserved
1754     )
1755 {
1756     LPURLCACHE_HEADER pHeader;
1757     struct _HASH_ENTRY * pHashEntry;
1758     CACHEFILE_ENTRY * pEntry;
1759     URL_CACHEFILE_ENTRY * pUrlEntry;
1760     URLCACHECONTAINER * pContainer;
1761
1762     TRACE("(%s, %p, %p, 0x%08x)\n",
1763         debugstr_a(lpszUrlName),
1764         lpCacheEntryInfo,
1765         lpdwCacheEntryInfoBufferSize,
1766         dwReserved);
1767
1768     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1769         return FALSE;
1770
1771     if (!URLCacheContainer_OpenIndex(pContainer))
1772         return FALSE;
1773
1774     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1775         return FALSE;
1776
1777     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1778     {
1779         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1780         TRACE("entry %s not found!\n", lpszUrlName);
1781         SetLastError(ERROR_FILE_NOT_FOUND);
1782         return FALSE;
1783     }
1784
1785     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1786     if (pEntry->dwSignature != URL_SIGNATURE)
1787     {
1788         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1789         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1790         SetLastError(ERROR_FILE_NOT_FOUND);
1791         return FALSE;
1792     }
1793
1794     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1795     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1796     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1797
1798     pUrlEntry->dwHitRate++;
1799     pUrlEntry->dwUseCount++;
1800     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1801
1802     if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1803     {
1804         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1805         return FALSE;
1806     }
1807     TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1808
1809     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1810
1811     return TRUE;
1812 }
1813
1814 /***********************************************************************
1815  *           RetrieveUrlCacheEntryFileW (WININET.@)
1816  *
1817  */
1818 BOOL WINAPI RetrieveUrlCacheEntryFileW(
1819     IN LPCWSTR lpszUrlName,
1820     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1821     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1822     IN DWORD dwReserved
1823     )
1824 {
1825     LPURLCACHE_HEADER pHeader;
1826     struct _HASH_ENTRY * pHashEntry;
1827     CACHEFILE_ENTRY * pEntry;
1828     URL_CACHEFILE_ENTRY * pUrlEntry;
1829     URLCACHECONTAINER * pContainer;
1830
1831     TRACE("(%s, %p, %p, 0x%08x)\n",
1832         debugstr_w(lpszUrlName),
1833         lpCacheEntryInfo,
1834         lpdwCacheEntryInfoBufferSize,
1835         dwReserved);
1836
1837     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1838         return FALSE;
1839
1840     if (!URLCacheContainer_OpenIndex(pContainer))
1841         return FALSE;
1842
1843     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1844         return FALSE;
1845
1846     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1847     {
1848         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1849         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1850         SetLastError(ERROR_FILE_NOT_FOUND);
1851         return FALSE;
1852     }
1853
1854     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1855     if (pEntry->dwSignature != URL_SIGNATURE)
1856     {
1857         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1858         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1859         SetLastError(ERROR_FILE_NOT_FOUND);
1860         return FALSE;
1861     }
1862
1863     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1864     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1865     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1866
1867     pUrlEntry->dwHitRate++;
1868     pUrlEntry->dwUseCount++;
1869     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1870
1871     if (!URLCache_CopyEntry(
1872         pContainer,
1873         pHeader,
1874         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1875         lpdwCacheEntryInfoBufferSize,
1876         pUrlEntry,
1877         TRUE /* UNICODE */))
1878     {
1879         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1880         return FALSE;
1881     }
1882     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1883
1884     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1885
1886     return TRUE;
1887 }
1888
1889 /***********************************************************************
1890  *           UnlockUrlCacheEntryFileA (WININET.@)
1891  *
1892  */
1893 BOOL WINAPI UnlockUrlCacheEntryFileA(
1894     IN LPCSTR lpszUrlName, 
1895     IN DWORD dwReserved
1896     )
1897 {
1898     LPURLCACHE_HEADER pHeader;
1899     struct _HASH_ENTRY * pHashEntry;
1900     CACHEFILE_ENTRY * pEntry;
1901     URL_CACHEFILE_ENTRY * pUrlEntry;
1902     URLCACHECONTAINER * pContainer;
1903
1904     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
1905
1906     if (dwReserved)
1907     {
1908         ERR("dwReserved != 0\n");
1909         SetLastError(ERROR_INVALID_PARAMETER);
1910         return FALSE;
1911     }
1912
1913     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1914        return FALSE;
1915
1916     if (!URLCacheContainer_OpenIndex(pContainer))
1917         return FALSE;
1918
1919     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1920         return FALSE;
1921
1922     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1923     {
1924         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1925         TRACE("entry %s not found!\n", lpszUrlName);
1926         SetLastError(ERROR_FILE_NOT_FOUND);
1927         return FALSE;
1928     }
1929
1930     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1931     if (pEntry->dwSignature != URL_SIGNATURE)
1932     {
1933         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1934         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1935         SetLastError(ERROR_FILE_NOT_FOUND);
1936         return FALSE;
1937     }
1938
1939     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1940
1941     if (pUrlEntry->dwUseCount == 0)
1942     {
1943         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1944         return FALSE;
1945     }
1946     pUrlEntry->dwUseCount--;
1947     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
1948
1949     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1950
1951     return TRUE;
1952 }
1953
1954 /***********************************************************************
1955  *           UnlockUrlCacheEntryFileW (WININET.@)
1956  *
1957  */
1958 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
1959 {
1960     LPURLCACHE_HEADER pHeader;
1961     struct _HASH_ENTRY * pHashEntry;
1962     CACHEFILE_ENTRY * pEntry;
1963     URL_CACHEFILE_ENTRY * pUrlEntry;
1964     URLCACHECONTAINER * pContainer;
1965
1966     TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
1967
1968     if (dwReserved)
1969     {
1970         ERR("dwReserved != 0\n");
1971         SetLastError(ERROR_INVALID_PARAMETER);
1972         return FALSE;
1973     }
1974
1975     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
1976        return FALSE;
1977
1978     if (!URLCacheContainer_OpenIndex(pContainer))
1979         return FALSE;
1980
1981     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1982         return FALSE;
1983
1984     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
1985     {
1986         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1987         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
1988         SetLastError(ERROR_FILE_NOT_FOUND);
1989         return FALSE;
1990     }
1991
1992     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1993     if (pEntry->dwSignature != URL_SIGNATURE)
1994     {
1995         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1996         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1997         SetLastError(ERROR_FILE_NOT_FOUND);
1998         return FALSE;
1999     }
2000
2001     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2002
2003     if (pUrlEntry->dwUseCount == 0)
2004     {
2005         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2006         return FALSE;
2007     }
2008     pUrlEntry->dwUseCount--;
2009     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2010
2011     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2012
2013     return TRUE;
2014 }
2015
2016 /***********************************************************************
2017  *           CreateUrlCacheEntryA (WININET.@)
2018  *
2019  */
2020 BOOL WINAPI CreateUrlCacheEntryA(
2021     IN LPCSTR lpszUrlName,
2022     IN DWORD dwExpectedFileSize,
2023     IN LPCSTR lpszFileExtension,
2024     OUT LPSTR lpszFileName,
2025     IN DWORD dwReserved
2026 )
2027 {
2028     DWORD len;
2029     WCHAR *url_name;
2030     WCHAR *file_extension;
2031     WCHAR file_name[MAX_PATH];
2032     BOOL bSuccess = FALSE;
2033     DWORD dwError = 0;
2034
2035     if ((len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0)) != 0 &&
2036         (url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2037     {
2038         MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2039         if ((len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0)) != 0 &&
2040             (file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR))) != 0)
2041         {
2042             MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2043             if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2044             {
2045                 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2046                 {
2047                     bSuccess = TRUE;
2048                 }
2049                 else
2050                 {
2051                     dwError = GetLastError();
2052                 }
2053             }
2054             else
2055             {
2056                 dwError = GetLastError();
2057             }
2058             HeapFree(GetProcessHeap(), 0, file_extension);
2059         }
2060         else
2061         {
2062             dwError = GetLastError();
2063         }
2064         HeapFree(GetProcessHeap(), 0, url_name);
2065         if (!bSuccess)
2066             SetLastError(dwError);
2067     }
2068     return bSuccess;
2069 }
2070 /***********************************************************************
2071  *           CreateUrlCacheEntryW (WININET.@)
2072  *
2073  */
2074 BOOL WINAPI CreateUrlCacheEntryW(
2075     IN LPCWSTR lpszUrlName,
2076     IN DWORD dwExpectedFileSize,
2077     IN LPCWSTR lpszFileExtension,
2078     OUT LPWSTR lpszFileName,
2079     IN DWORD dwReserved
2080 )
2081 {
2082     URLCACHECONTAINER * pContainer;
2083     LPURLCACHE_HEADER pHeader;
2084     CHAR szFile[MAX_PATH];
2085     WCHAR szExtension[MAX_PATH];
2086     LPCWSTR lpszUrlPart;
2087     LPCWSTR lpszUrlEnd;
2088     LPCWSTR lpszFileNameExtension;
2089     LPWSTR lpszFileNameNoPath;
2090     int i;
2091     int countnoextension;
2092     BYTE CacheDir;
2093     LONG lBufferSize;
2094     BOOL bFound = FALSE;
2095     int count;
2096     static const WCHAR szWWW[] = {'w','w','w',0};
2097
2098     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2099         debugstr_w(lpszUrlName),
2100         dwExpectedFileSize,
2101         debugstr_w(lpszFileExtension),
2102         lpszFileName,
2103         dwReserved);
2104
2105     if (dwReserved)
2106     {
2107         ERR("dwReserved != 0\n");
2108         SetLastError(ERROR_INVALID_PARAMETER);
2109         return FALSE;
2110     }
2111
2112    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2113     
2114     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2115         lpszUrlEnd--;
2116
2117     for (lpszUrlPart = lpszUrlEnd; 
2118         (lpszUrlPart >= lpszUrlName); 
2119         lpszUrlPart--)
2120     {
2121         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2122         {
2123             bFound = TRUE;
2124             lpszUrlPart++;
2125             break;
2126         }
2127         else if(*lpszUrlPart == '?' || *lpszUrlPart == '#')
2128         {
2129             lpszUrlEnd = lpszUrlPart;
2130         }
2131     }
2132     if (!lstrcmpW(lpszUrlPart, szWWW))
2133     {
2134         lpszUrlPart += lstrlenW(szWWW);
2135     }
2136
2137     count = lpszUrlEnd - lpszUrlPart;
2138
2139     if (bFound && (count < MAX_PATH))
2140     {
2141         int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2142         if (!len)
2143             return FALSE;
2144         szFile[len] = '\0';
2145         /* FIXME: get rid of illegal characters like \, / and : */
2146     }
2147     else
2148     {
2149         FIXME("need to generate a random filename\n");
2150     }
2151
2152     TRACE("File name: %s\n", debugstr_a(szFile));
2153
2154     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2155         return FALSE;
2156
2157     if (!URLCacheContainer_OpenIndex(pContainer))
2158         return FALSE;
2159
2160     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2161         return FALSE;
2162
2163     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2164
2165     lBufferSize = MAX_PATH * sizeof(WCHAR);
2166     URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
2167
2168     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2169
2170     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2171         lpszFileNameNoPath >= lpszFileName; 
2172         --lpszFileNameNoPath)
2173     {
2174         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2175             break;
2176     }
2177
2178     countnoextension = lstrlenW(lpszFileNameNoPath);
2179     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2180     if (lpszFileNameExtension)
2181         countnoextension -= lstrlenW(lpszFileNameExtension);
2182     *szExtension = '\0';
2183
2184     if (lpszFileExtension)
2185     {
2186         szExtension[0] = '.';
2187         lstrcpyW(szExtension+1, lpszFileExtension);
2188     }
2189
2190     for (i = 0; i < 255; i++)
2191     {
2192         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2193         HANDLE hFile;
2194         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2195         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2196         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2197         if (hFile != INVALID_HANDLE_VALUE)
2198         {
2199             CloseHandle(hFile);
2200             return TRUE;
2201         }
2202     }
2203
2204     return FALSE;
2205 }
2206
2207
2208 /***********************************************************************
2209  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
2210  *
2211  *   The bug we are compensating for is that some drongo at Microsoft
2212  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2213  *   As a consequence, CommitUrlCacheEntryA has been effectively
2214  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2215  *   is still defined as LPCWSTR. The result (other than madness) is
2216  *   that we always need to store lpHeaderInfo in CP_ACP rather than
2217  *   in UTF16, and we need to avoid converting lpHeaderInfo in
2218  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2219  *   result will lose data for arbitrary binary data.
2220  *
2221  */
2222 static BOOL WINAPI CommitUrlCacheEntryInternal(
2223     IN LPCWSTR lpszUrlName,
2224     IN LPCWSTR lpszLocalFileName,
2225     IN FILETIME ExpireTime,
2226     IN FILETIME LastModifiedTime,
2227     IN DWORD CacheEntryType,
2228     IN LPBYTE lpHeaderInfo,
2229     IN DWORD dwHeaderSize,
2230     IN LPCWSTR lpszFileExtension,
2231     IN LPCWSTR lpszOriginalUrl
2232     )
2233 {
2234     URLCACHECONTAINER * pContainer;
2235     LPURLCACHE_HEADER pHeader;
2236     struct _HASH_ENTRY * pHashEntry;
2237     CACHEFILE_ENTRY * pEntry;
2238     URL_CACHEFILE_ENTRY * pUrlEntry;
2239     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2240     DWORD dwOffsetLocalFileName = 0;
2241     DWORD dwOffsetHeader = 0;
2242     DWORD dwOffsetFileExtension = 0;
2243     DWORD dwFileSizeLow = 0;
2244     DWORD dwFileSizeHigh = 0;
2245     BYTE cDirectory = 0;
2246     int len;
2247     char achFile[MAX_PATH];
2248     LPSTR lpszUrlNameA = NULL;
2249     LPSTR lpszFileExtensionA = NULL;
2250     char *pchLocalFileName = 0;
2251     DWORD error = ERROR_SUCCESS;
2252
2253     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2254         debugstr_w(lpszUrlName),
2255         debugstr_w(lpszLocalFileName),
2256         CacheEntryType,
2257         lpHeaderInfo,
2258         dwHeaderSize,
2259         debugstr_w(lpszFileExtension),
2260         debugstr_w(lpszOriginalUrl));
2261
2262     if (lpszOriginalUrl)
2263         WARN(": lpszOriginalUrl ignored\n");
2264  
2265     if (lpszLocalFileName)
2266     {
2267         HANDLE hFile;
2268
2269         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2270         if (hFile == INVALID_HANDLE_VALUE)
2271         {
2272             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2273             return FALSE;
2274         }
2275
2276         /* Get file size */
2277         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2278         if ((dwFileSizeLow == INVALID_FILE_SIZE) && (GetLastError() != NO_ERROR))
2279         {
2280             ERR("couldn't get file size (error is %d)\n", GetLastError());
2281             CloseHandle(hFile);
2282             return FALSE;
2283         }
2284
2285         CloseHandle(hFile);
2286     }
2287
2288     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2289         return FALSE;
2290
2291     if (!URLCacheContainer_OpenIndex(pContainer))
2292         return FALSE;
2293
2294     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2295         return FALSE;
2296
2297     len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2298     lpszUrlNameA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2299     if (!lpszUrlNameA)
2300     {
2301         error = GetLastError();
2302         goto cleanup;
2303     }
2304     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, lpszUrlNameA, len, NULL, NULL);
2305
2306     if (lpszFileExtension)
2307     {
2308         len = WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, NULL, 0, NULL, NULL);
2309         lpszFileExtensionA = HeapAlloc(GetProcessHeap(), 0, len * sizeof(char));
2310         if (!lpszFileExtensionA)
2311         {
2312             error = GetLastError();
2313             goto cleanup;
2314         }
2315         WideCharToMultiByte(CP_ACP, 0, lpszFileExtension, -1, lpszFileExtensionA, len, NULL, NULL);
2316     }
2317
2318     if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2319     {
2320         FIXME("entry already in cache - don't know what to do!\n");
2321 /*
2322  *        SetLastError(ERROR_FILE_NOT_FOUND);
2323  *        return FALSE;
2324  */
2325         goto cleanup;
2326     }
2327
2328     if (lpszLocalFileName)
2329     {
2330         BOOL bFound = FALSE;
2331
2332         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2333         {
2334             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2335             error = ERROR_INVALID_PARAMETER;
2336             goto cleanup;
2337         }
2338
2339         /* skip container path prefix */
2340         lpszLocalFileName += lstrlenW(pContainer->path);
2341
2342         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, -1, NULL, NULL);
2343         pchLocalFileName = achFile;
2344
2345         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2346         {
2347             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2348             {
2349                 bFound = TRUE;
2350                 break;
2351             }
2352         }
2353
2354         if (!bFound)
2355         {
2356             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2357             error = ERROR_INVALID_PARAMETER;
2358             goto cleanup;
2359         }
2360
2361         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2362     }
2363
2364     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2365     if (lpszLocalFileName)
2366     {
2367         len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2368         dwOffsetLocalFileName = dwBytesNeeded;
2369         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2370     }
2371     if (lpHeaderInfo)
2372     {
2373         dwOffsetHeader = dwBytesNeeded;
2374         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2375     }
2376     if (lpszFileExtensionA)
2377     {
2378         dwOffsetFileExtension = dwBytesNeeded;
2379         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2380     }
2381
2382     /* round up to next block */
2383     if (dwBytesNeeded % BLOCKSIZE)
2384     {
2385         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2386         dwBytesNeeded += BLOCKSIZE;
2387     }
2388
2389     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2390     {
2391         ERR("no free entries\n");
2392         error = ERROR_DISK_FULL;
2393         goto cleanup;
2394     }
2395
2396     /* FindFirstFreeEntry fills in blocks used */
2397     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2398     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2399     pUrlEntry->CacheDir = cDirectory;
2400     pUrlEntry->CacheEntryType = CacheEntryType;
2401     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2402     pUrlEntry->dwExemptDelta = 0;
2403     pUrlEntry->dwHitRate = 0;
2404     pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2405     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2406     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2407     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2408     pUrlEntry->dwSizeHigh = 0;
2409     pUrlEntry->dwSizeLow = dwFileSizeLow;
2410     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
2411     pUrlEntry->dwUseCount = 0;
2412     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2413     pUrlEntry->LastModifiedTime = LastModifiedTime;
2414     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2415     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2416     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2417     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2418
2419     /*** Unknowns ***/
2420     pUrlEntry->dwUnknown1 = 0;
2421     pUrlEntry->dwUnknown2 = 0;
2422     pUrlEntry->dwUnknown3 = 0x60;
2423     pUrlEntry->Unknown4 = 0;
2424     pUrlEntry->wUnknown5 = 0x1010;
2425     pUrlEntry->dwUnknown7 = 0;
2426     pUrlEntry->dwUnknown8 = 0;
2427
2428
2429     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2430     if (dwOffsetLocalFileName)
2431         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2432     if (dwOffsetHeader)
2433         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2434     if (dwOffsetFileExtension)
2435         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2436
2437     if (!URLCache_AddEntryToHash(pHeader, lpszUrlNameA, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
2438     {
2439         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2440         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2441         HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2442         return FALSE;
2443     }
2444
2445 cleanup:
2446     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2447     HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2448     HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2449
2450     if (error == ERROR_SUCCESS)
2451         return TRUE;
2452     else
2453     {
2454         SetLastError(error);
2455         return FALSE;
2456     }
2457 }
2458
2459 /***********************************************************************
2460  *           CommitUrlCacheEntryA (WININET.@)
2461  *
2462  */
2463 BOOL WINAPI CommitUrlCacheEntryA(
2464     IN LPCSTR lpszUrlName,
2465     IN LPCSTR lpszLocalFileName,
2466     IN FILETIME ExpireTime,
2467     IN FILETIME LastModifiedTime,
2468     IN DWORD CacheEntryType,
2469     IN LPBYTE lpHeaderInfo,
2470     IN DWORD dwHeaderSize,
2471     IN LPCSTR lpszFileExtension,
2472     IN LPCSTR lpszOriginalUrl
2473     )
2474 {
2475     DWORD len;
2476     WCHAR *url_name = NULL;
2477     WCHAR *local_file_name = NULL;
2478     WCHAR *original_url = NULL;
2479     WCHAR *file_extension = NULL;
2480     BOOL bSuccess = FALSE;
2481
2482     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2483         debugstr_a(lpszUrlName),
2484         debugstr_a(lpszLocalFileName),
2485         CacheEntryType,
2486         lpHeaderInfo,
2487         dwHeaderSize,
2488         debugstr_a(lpszFileExtension),
2489         debugstr_a(lpszOriginalUrl));
2490
2491     len = MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, NULL, 0);
2492     url_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2493     if (!url_name)
2494         goto cleanup;
2495     MultiByteToWideChar(CP_ACP, 0, lpszUrlName, -1, url_name, len);
2496
2497     if (lpszLocalFileName)
2498     {
2499         len = MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, NULL, 0);
2500         local_file_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2501         if (!local_file_name)
2502             goto cleanup;
2503         MultiByteToWideChar(CP_ACP, 0, lpszLocalFileName, -1, local_file_name, len);
2504     }
2505     if (lpszFileExtension)
2506     {
2507         len = MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, NULL, 0);
2508         file_extension = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2509         if (!file_extension)
2510             goto cleanup;
2511         MultiByteToWideChar(CP_ACP, 0, lpszFileExtension, -1, file_extension, len);
2512     }
2513     if (lpszOriginalUrl)
2514     {
2515         len = MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, NULL, 0);
2516         original_url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2517         if (!original_url)
2518             goto cleanup;
2519         MultiByteToWideChar(CP_ACP, 0, lpszOriginalUrl, -1, original_url, len);
2520     }
2521
2522     bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2523                                            CacheEntryType, lpHeaderInfo, dwHeaderSize,
2524                                            file_extension, original_url);
2525
2526 cleanup:
2527     HeapFree(GetProcessHeap(), 0, original_url);
2528     HeapFree(GetProcessHeap(), 0, file_extension);
2529     HeapFree(GetProcessHeap(), 0, local_file_name);
2530     HeapFree(GetProcessHeap(), 0, url_name);
2531
2532     return bSuccess;
2533 }
2534
2535 /***********************************************************************
2536  *           CommitUrlCacheEntryW (WININET.@)
2537  *
2538  */
2539 BOOL WINAPI CommitUrlCacheEntryW(
2540     IN LPCWSTR lpszUrlName,
2541     IN LPCWSTR lpszLocalFileName,
2542     IN FILETIME ExpireTime,
2543     IN FILETIME LastModifiedTime,
2544     IN DWORD CacheEntryType,
2545     IN LPWSTR lpHeaderInfo,
2546     IN DWORD dwHeaderSize,
2547     IN LPCWSTR lpszFileExtension,
2548     IN LPCWSTR lpszOriginalUrl
2549     )
2550 {
2551     DWORD dwError = 0;
2552     BOOL bSuccess = FALSE;
2553     DWORD len = 0;
2554     CHAR *header_info = NULL;
2555
2556     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2557         debugstr_w(lpszUrlName),
2558         debugstr_w(lpszLocalFileName),
2559         CacheEntryType,
2560         lpHeaderInfo,
2561         dwHeaderSize,
2562         debugstr_w(lpszFileExtension),
2563         debugstr_w(lpszOriginalUrl));
2564
2565     if (!lpHeaderInfo ||
2566         ((len = WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, NULL, 0, NULL, NULL)) != 0 &&
2567          (header_info = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) * len)) != 0))
2568     {
2569         if (header_info)
2570             WideCharToMultiByte(CP_ACP, 0, lpHeaderInfo, -1, header_info, len, NULL, NULL);
2571         if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2572                                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2573         {
2574                 bSuccess = TRUE;
2575         }
2576         else
2577         {
2578                 dwError = GetLastError();
2579         }
2580         if (header_info)
2581         {
2582             HeapFree(GetProcessHeap(), 0, header_info);
2583             if (!bSuccess)
2584                 SetLastError(dwError);
2585         }
2586     }
2587     return bSuccess;
2588 }
2589
2590 /***********************************************************************
2591  *           ReadUrlCacheEntryStream (WININET.@)
2592  *
2593  */
2594 BOOL WINAPI ReadUrlCacheEntryStream(
2595     IN HANDLE hUrlCacheStream,
2596     IN  DWORD dwLocation,
2597     IN OUT LPVOID lpBuffer,
2598     IN OUT LPDWORD lpdwLen,
2599     IN DWORD dwReserved
2600     )
2601 {
2602     /* Get handle to file from 'stream' */
2603     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2604
2605     if (dwReserved != 0)
2606     {
2607         ERR("dwReserved != 0\n");
2608         SetLastError(ERROR_INVALID_PARAMETER);
2609         return FALSE;
2610     }
2611
2612     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2613     {
2614         SetLastError(ERROR_INVALID_HANDLE);
2615         return FALSE;
2616     }
2617
2618     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2619         return FALSE;
2620     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2621 }
2622
2623 /***********************************************************************
2624  *           RetrieveUrlCacheEntryStreamA (WININET.@)
2625  *
2626  */
2627 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2628     IN LPCSTR lpszUrlName,
2629     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2630     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2631     IN BOOL fRandomRead,
2632     IN DWORD dwReserved
2633     )
2634 {
2635     /* NOTE: this is not the same as the way that the native
2636      * version allocates 'stream' handles. I did it this way
2637      * as it is much easier and no applications should depend
2638      * on this behaviour. (Native version appears to allocate
2639      * indices into a table)
2640      */
2641     STREAM_HANDLE * pStream;
2642     HANDLE hFile;
2643
2644     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2645            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2646
2647     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2648         lpCacheEntryInfo,
2649         lpdwCacheEntryInfoBufferSize,
2650         dwReserved))
2651     {
2652         return NULL;
2653     }
2654
2655     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2656         GENERIC_READ,
2657         FILE_SHARE_READ,
2658         NULL,
2659         OPEN_EXISTING,
2660         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
2661         NULL);
2662     if (hFile == INVALID_HANDLE_VALUE)
2663         return FALSE;
2664     
2665     /* allocate handle storage space */
2666     pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
2667     if (!pStream)
2668     {
2669         CloseHandle(hFile);
2670         SetLastError(ERROR_OUTOFMEMORY);
2671         return FALSE;
2672     }
2673
2674     pStream->hFile = hFile;
2675     strcpy(pStream->lpszUrl, lpszUrlName);
2676     return (HANDLE)pStream;
2677 }
2678
2679 /***********************************************************************
2680  *           RetrieveUrlCacheEntryStreamW (WININET.@)
2681  *
2682  */
2683 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
2684     IN LPCWSTR lpszUrlName,
2685     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2686     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2687     IN BOOL fRandomRead,
2688     IN DWORD dwReserved
2689     )
2690 {
2691     FIXME( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
2692            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2693     return NULL;
2694 }
2695
2696 /***********************************************************************
2697  *           UnlockUrlCacheEntryStream (WININET.@)
2698  *
2699  */
2700 BOOL WINAPI UnlockUrlCacheEntryStream(
2701     IN HANDLE hUrlCacheStream,
2702     IN DWORD dwReserved
2703 )
2704 {
2705     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2706
2707     if (dwReserved != 0)
2708     {
2709         ERR("dwReserved != 0\n");
2710         SetLastError(ERROR_INVALID_PARAMETER);
2711         return FALSE;
2712     }
2713
2714     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2715     {
2716         SetLastError(ERROR_INVALID_HANDLE);
2717         return FALSE;
2718     }
2719
2720     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
2721         return FALSE;
2722
2723     /* close file handle */
2724     CloseHandle(pStream->hFile);
2725
2726     /* free allocated space */
2727     HeapFree(GetProcessHeap(), 0, pStream);
2728
2729     return TRUE;
2730 }
2731
2732
2733 /***********************************************************************
2734  *           DeleteUrlCacheEntryA (WININET.@)
2735  *
2736  */
2737 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2738 {
2739     URLCACHECONTAINER * pContainer;
2740     LPURLCACHE_HEADER pHeader;
2741     struct _HASH_ENTRY * pHashEntry;
2742     CACHEFILE_ENTRY * pEntry;
2743
2744     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2745
2746     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2747         return FALSE;
2748
2749     if (!URLCacheContainer_OpenIndex(pContainer))
2750         return FALSE;
2751
2752     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2753         return FALSE;
2754
2755     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2756     {
2757         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2758         TRACE("entry %s not found!\n", lpszUrlName);
2759         SetLastError(ERROR_FILE_NOT_FOUND);
2760         return FALSE;
2761     }
2762
2763     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2764     URLCache_DeleteEntry(pHeader, pEntry);
2765
2766     URLCache_DeleteEntryFromHash(pHashEntry);
2767
2768     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2769
2770     return TRUE;
2771 }
2772
2773 /***********************************************************************
2774  *           DeleteUrlCacheEntryW (WININET.@)
2775  *
2776  */
2777 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
2778 {
2779     URLCACHECONTAINER * pContainer;
2780     LPURLCACHE_HEADER pHeader;
2781     struct _HASH_ENTRY * pHashEntry;
2782     CACHEFILE_ENTRY * pEntry;
2783     LPSTR urlA;
2784     int url_len;
2785
2786     TRACE("(%s)\n", debugstr_w(lpszUrlName));
2787
2788     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
2789     urlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
2790     if (!urlA)
2791     {
2792         SetLastError(ERROR_OUTOFMEMORY);
2793         return FALSE;
2794     }
2795     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, urlA, url_len, NULL, NULL);
2796
2797     if (!URLCacheContainers_FindContainerW(lpszUrlName, &pContainer))
2798     {
2799         HeapFree(GetProcessHeap(), 0, urlA);
2800         return FALSE;
2801     }
2802     if (!URLCacheContainer_OpenIndex(pContainer))
2803     {
2804         HeapFree(GetProcessHeap(), 0, urlA);
2805         return FALSE;
2806     }
2807     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2808     {
2809         HeapFree(GetProcessHeap(), 0, urlA);
2810         return FALSE;
2811     }
2812
2813     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
2814     {
2815         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2816         TRACE("entry %s not found!\n", debugstr_a(urlA));
2817         HeapFree(GetProcessHeap(), 0, urlA);
2818         SetLastError(ERROR_FILE_NOT_FOUND);
2819         return FALSE;
2820     }
2821
2822     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2823     URLCache_DeleteEntry(pHeader, pEntry);
2824
2825     URLCache_DeleteEntryFromHash(pHashEntry);
2826
2827     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2828
2829     HeapFree(GetProcessHeap(), 0, urlA);
2830     return TRUE;
2831 }
2832
2833 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
2834 {
2835     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2836     return TRUE;
2837 }
2838
2839 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
2840 {
2841     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
2842     return TRUE;
2843 }
2844
2845 /***********************************************************************
2846  *           CreateCacheContainerA (WININET.@)
2847  */
2848 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2849                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2850 {
2851     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2852           d1, d2, d3, d4, d5, d6, d7, d8);
2853     return TRUE;
2854 }
2855
2856 /***********************************************************************
2857  *           CreateCacheContainerW (WININET.@)
2858  */
2859 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
2860                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
2861 {
2862     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
2863           d1, d2, d3, d4, d5, d6, d7, d8);
2864     return TRUE;
2865 }
2866
2867 /***********************************************************************
2868  *           FindFirstUrlCacheContainerA (WININET.@)
2869  */
2870 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2871 {
2872     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2873     return NULL;
2874 }
2875
2876 /***********************************************************************
2877  *           FindFirstUrlCacheContainerW (WININET.@)
2878  */
2879 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
2880 {
2881     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
2882     return NULL;
2883 }
2884
2885 /***********************************************************************
2886  *           FindNextUrlCacheContainerA (WININET.@)
2887  */
2888 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
2889 {
2890     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2891     return FALSE;
2892 }
2893
2894 /***********************************************************************
2895  *           FindNextUrlCacheContainerW (WININET.@)
2896  */
2897 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
2898 {
2899     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
2900     return FALSE;
2901 }
2902
2903 HANDLE WINAPI FindFirstUrlCacheEntryExA(
2904   LPCSTR lpszUrlSearchPattern,
2905   DWORD dwFlags,
2906   DWORD dwFilter,
2907   GROUPID GroupId,
2908   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
2909   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2910   LPVOID lpReserved,
2911   LPDWORD pcbReserved2,
2912   LPVOID lpReserved3
2913 )
2914 {
2915     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
2916           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2917           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2918     SetLastError(ERROR_FILE_NOT_FOUND);
2919     return NULL;
2920 }
2921
2922 HANDLE WINAPI FindFirstUrlCacheEntryExW(
2923   LPCWSTR lpszUrlSearchPattern,
2924   DWORD dwFlags,
2925   DWORD dwFilter,
2926   GROUPID GroupId,
2927   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
2928   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
2929   LPVOID lpReserved,
2930   LPDWORD pcbReserved2,
2931   LPVOID lpReserved3
2932 )
2933 {
2934     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
2935           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
2936           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
2937     SetLastError(ERROR_FILE_NOT_FOUND);
2938     return NULL;
2939 }
2940
2941 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
2942
2943 typedef struct URLCacheFindEntryHandle
2944 {
2945     DWORD dwMagic;
2946     LPWSTR lpszUrlSearchPattern;
2947     DWORD dwContainerIndex;
2948     DWORD dwHashTableIndex;
2949     DWORD dwHashEntryIndex;
2950 } URLCacheFindEntryHandle;
2951
2952 /***********************************************************************
2953  *           FindFirstUrlCacheEntryA (WININET.@)
2954  *
2955  */
2956 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2957  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2958 {
2959     URLCacheFindEntryHandle *pEntryHandle;
2960
2961     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2962
2963     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
2964     if (!pEntryHandle)
2965         return NULL;
2966
2967     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
2968     if (lpszUrlSearchPattern)
2969     {
2970         int len = MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, NULL, 0);
2971         pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
2972         if (!pEntryHandle->lpszUrlSearchPattern)
2973         {
2974             HeapFree(GetProcessHeap(), 0, pEntryHandle);
2975             return NULL;
2976         }
2977         MultiByteToWideChar(CP_ACP, 0, lpszUrlSearchPattern, -1, pEntryHandle->lpszUrlSearchPattern, len);
2978     }
2979     else
2980         pEntryHandle->lpszUrlSearchPattern = NULL;
2981     pEntryHandle->dwContainerIndex = 0;
2982     pEntryHandle->dwHashTableIndex = 0;
2983     pEntryHandle->dwHashEntryIndex = 0;
2984
2985     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
2986     {
2987         HeapFree(GetProcessHeap(), 0, pEntryHandle);
2988         return NULL;
2989     }
2990     return pEntryHandle;
2991 }
2992
2993 /***********************************************************************
2994  *           FindFirstUrlCacheEntryW (WININET.@)
2995  *
2996  */
2997 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2998  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2999 {
3000     URLCacheFindEntryHandle *pEntryHandle;
3001
3002     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3003
3004     pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3005     if (!pEntryHandle)
3006         return NULL;
3007
3008     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3009     if (lpszUrlSearchPattern)
3010     {
3011         int len = strlenW(lpszUrlSearchPattern);
3012         pEntryHandle->lpszUrlSearchPattern = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
3013         if (!pEntryHandle->lpszUrlSearchPattern)
3014         {
3015             HeapFree(GetProcessHeap(), 0, pEntryHandle);
3016             return NULL;
3017         }
3018         memcpy(pEntryHandle->lpszUrlSearchPattern, lpszUrlSearchPattern, (len + 1) * sizeof(WCHAR));
3019     }
3020     else
3021         pEntryHandle->lpszUrlSearchPattern = NULL;
3022     pEntryHandle->dwContainerIndex = 0;
3023     pEntryHandle->dwHashTableIndex = 0;
3024     pEntryHandle->dwHashEntryIndex = 0;
3025
3026     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3027     {
3028         HeapFree(GetProcessHeap(), 0, pEntryHandle);
3029         return NULL;
3030     }
3031     return pEntryHandle;
3032 }
3033
3034 /***********************************************************************
3035  *           FindNextUrlCacheEntryA (WININET.@)
3036  */
3037 BOOL WINAPI FindNextUrlCacheEntryA(
3038   HANDLE hEnumHandle,
3039   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3040   LPDWORD lpdwNextCacheEntryInfoBufferSize)
3041 {
3042     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3043     URLCACHECONTAINER * pContainer;
3044
3045     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3046
3047     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3048     {
3049         SetLastError(ERROR_INVALID_HANDLE);
3050         return FALSE;
3051     }
3052
3053     for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3054          pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3055     {
3056         LPURLCACHE_HEADER pHeader;
3057         HASH_CACHEFILE_ENTRY *pHashTableEntry;
3058
3059         if (!URLCacheContainer_OpenIndex(pContainer))
3060             return FALSE;
3061
3062         if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3063             return FALSE;
3064
3065         for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3066              pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3067         {
3068             const struct _HASH_ENTRY *pHashEntry = NULL;
3069             for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3070                  pEntryHandle->dwHashEntryIndex++)
3071             {
3072                 const URL_CACHEFILE_ENTRY *pUrlEntry;
3073                 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3074
3075                 if (pEntry->dwSignature != URL_SIGNATURE)
3076                     continue;
3077
3078                 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3079                 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
3080                 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
3081
3082                 if (!URLCache_CopyEntry(
3083                     pContainer,
3084                     pHeader,
3085                     lpNextCacheEntryInfo,
3086                     lpdwNextCacheEntryInfoBufferSize,
3087                     pUrlEntry,
3088                     FALSE /* not UNICODE */))
3089                 {
3090                     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3091                     return FALSE;
3092                 }
3093                 TRACE("Local File Name: %s\n", debugstr_a(lpNextCacheEntryInfo->lpszLocalFileName));
3094
3095                 /* increment the current index so that next time the function
3096                  * is called the next entry is returned */
3097                 pEntryHandle->dwHashEntryIndex++;
3098                 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3099                 return TRUE;
3100             }
3101         }
3102
3103         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3104     }
3105
3106     SetLastError(ERROR_NO_MORE_ITEMS);
3107     return FALSE;
3108 }
3109
3110 BOOL WINAPI FindNextUrlCacheEntryW(
3111   HANDLE hEnumHandle,
3112   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3113   LPDWORD lpdwNextCacheEntryInfoBufferSize
3114 )
3115 {
3116     FIXME("(%p, %p, %p) stub\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3117     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3118     return FALSE;
3119 }
3120
3121 /***********************************************************************
3122  *           FindCloseUrlCache (WININET.@)
3123  */
3124 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3125 {
3126     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3127
3128     TRACE("(%p)\n", hEnumHandle);
3129
3130     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3131     {
3132         SetLastError(ERROR_INVALID_HANDLE);
3133         return FALSE;
3134     }
3135
3136     pEntryHandle->dwMagic = 0;
3137     HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3138     HeapFree(GetProcessHeap(), 0, pEntryHandle);
3139
3140     return TRUE;
3141 }
3142
3143 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3144                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3145 {
3146     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3147           dwSearchCondition, lpGroupId, lpReserved);
3148     return NULL;
3149 }
3150
3151 BOOL WINAPI FindNextUrlCacheEntryExA(
3152   HANDLE hEnumHandle,
3153   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3154   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3155   LPVOID lpReserved,
3156   LPDWORD pcbReserved2,
3157   LPVOID lpReserved3
3158 )
3159 {
3160     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3161           lpReserved, pcbReserved2, lpReserved3);
3162     return FALSE;
3163 }
3164
3165 BOOL WINAPI FindNextUrlCacheEntryExW(
3166   HANDLE hEnumHandle,
3167   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3168   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3169   LPVOID lpReserved,
3170   LPDWORD pcbReserved2,
3171   LPVOID lpReserved3
3172 )
3173 {
3174     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3175           lpReserved, pcbReserved2, lpReserved3);
3176     return FALSE;
3177 }
3178
3179 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3180 {
3181     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3182     return FALSE;
3183 }
3184
3185 /***********************************************************************
3186  *           CreateUrlCacheGroup (WININET.@)
3187  *
3188  */
3189 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3190 {
3191   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3192   return FALSE;
3193 }
3194
3195 /***********************************************************************
3196  *           DeleteUrlCacheGroup (WININET.@)
3197  *
3198  */
3199 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3200 {
3201     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3202           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3203     return FALSE;
3204 }
3205
3206 /***********************************************************************
3207  *           SetUrlCacheEntryGroupA (WININET.@)
3208  *
3209  */
3210 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3211   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3212   LPVOID lpReserved)
3213 {
3214     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3215           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3216           pbGroupAttributes, cbGroupAttributes, lpReserved);
3217     SetLastError(ERROR_FILE_NOT_FOUND);
3218     return FALSE;
3219 }
3220
3221 /***********************************************************************
3222  *           SetUrlCacheEntryGroupW (WININET.@)
3223  *
3224  */
3225 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3226   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3227   LPVOID lpReserved)
3228 {
3229     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3230           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3231           pbGroupAttributes, cbGroupAttributes, lpReserved);
3232     SetLastError(ERROR_FILE_NOT_FOUND);
3233     return FALSE;
3234 }
3235
3236 /***********************************************************************
3237  *           GetUrlCacheConfigInfoW (WININET.@)
3238  */
3239 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3240 {
3241     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3242     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3243     return FALSE;
3244 }
3245
3246 /***********************************************************************
3247  *           GetUrlCacheConfigInfoA (WININET.@)
3248  *
3249  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
3250  */
3251 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3252 {
3253     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3254     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3255     return FALSE;
3256 }
3257
3258 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3259                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3260                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3261 {
3262     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3263           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3264           lpdwGroupInfo, lpReserved);
3265     return FALSE;
3266 }
3267
3268 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3269                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3270                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3271 {
3272     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3273           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3274           lpdwGroupInfo, lpReserved);
3275     return FALSE;
3276 }
3277
3278 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3279                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3280 {
3281     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3282           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3283     return TRUE;
3284 }
3285
3286 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3287                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3288 {
3289     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3290           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3291     return TRUE;
3292 }
3293
3294 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3295 {
3296     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3297     return TRUE;
3298 }
3299
3300 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3301 {
3302     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3303     return TRUE;
3304 }
3305
3306 /***********************************************************************
3307  *           DeleteIE3Cache (WININET.@)
3308  *
3309  * Deletes the files used by the IE3 URL caching system.
3310  *
3311  * PARAMS
3312  *   hWnd        [I] A dummy window.
3313  *   hInst       [I] Instance of process calling the function.
3314  *   lpszCmdLine [I] Options used by function.
3315  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
3316  */
3317 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3318 {
3319     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3320     return 0;
3321 }
3322
3323 /***********************************************************************
3324  *           IsUrlCacheEntryExpiredA (WININET.@)
3325  *
3326  * PARAMS
3327  *   url             [I] Url
3328  *   dwFlags         [I] Unknown
3329  *   pftLastModified [O] Last modified time
3330  */
3331 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3332 {
3333     LPURLCACHE_HEADER pHeader;
3334     struct _HASH_ENTRY * pHashEntry;
3335     const CACHEFILE_ENTRY * pEntry;
3336     const URL_CACHEFILE_ENTRY * pUrlEntry;
3337     URLCACHECONTAINER * pContainer;
3338
3339     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3340
3341     if (!URLCacheContainers_FindContainerA(url, &pContainer))
3342         return FALSE;
3343
3344     if (!URLCacheContainer_OpenIndex(pContainer))
3345         return FALSE;
3346
3347     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3348         return FALSE;
3349
3350     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3351     {
3352         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3353         TRACE("entry %s not found!\n", url);
3354         SetLastError(ERROR_FILE_NOT_FOUND);
3355         return FALSE;
3356     }
3357
3358     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3359     if (pEntry->dwSignature != URL_SIGNATURE)
3360     {
3361         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3362         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3363         SetLastError(ERROR_FILE_NOT_FOUND);
3364         return FALSE;
3365     }
3366
3367     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3368
3369     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3370
3371     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3372
3373     return TRUE;
3374 }
3375
3376 /***********************************************************************
3377  *           IsUrlCacheEntryExpiredW (WININET.@)
3378  *
3379  * PARAMS
3380  *   url             [I] Url
3381  *   dwFlags         [I] Unknown
3382  *   pftLastModified [O] Last modified time
3383  */
3384 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3385 {
3386     LPURLCACHE_HEADER pHeader;
3387     struct _HASH_ENTRY * pHashEntry;
3388     const CACHEFILE_ENTRY * pEntry;
3389     const URL_CACHEFILE_ENTRY * pUrlEntry;
3390     URLCACHECONTAINER * pContainer;
3391
3392     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3393
3394     if (!URLCacheContainers_FindContainerW(url, &pContainer))
3395         return FALSE;
3396
3397     if (!URLCacheContainer_OpenIndex(pContainer))
3398         return FALSE;
3399
3400     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3401         return FALSE;
3402
3403     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3404     {
3405         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3406         TRACE("entry %s not found!\n", debugstr_w(url));
3407         SetLastError(ERROR_FILE_NOT_FOUND);
3408         return FALSE;
3409     }
3410
3411     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3412     if (pEntry->dwSignature != URL_SIGNATURE)
3413     {
3414         URLCacheContainer_UnlockIndex(pContainer, pHeader);
3415         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
3416         SetLastError(ERROR_FILE_NOT_FOUND);
3417         return FALSE;
3418     }
3419
3420     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3421
3422     DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, pftLastModified);
3423
3424     URLCacheContainer_UnlockIndex(pContainer, pHeader);
3425
3426     return TRUE;
3427 }