Remove URLCache_FindEntry() which is not used any longer.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #define COM_NO_WINDOWS_H
26 #include "config.h"
27
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "wininet.h"
38 #include "winerror.h"
39 #include "internet.h"
40 #include "winreg.h"
41 #include "shlwapi.h"
42 #include "wingdi.h"
43 #include "shlobj.h"
44
45 #include "wine/unicode.h"
46 #include "wine/list.h"
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
50
51 #define ENTRY_START_OFFSET  0x4000
52 #define DIR_LENGTH          8
53 #define BLOCKSIZE           128
54 #define HASHTABLE_SIZE      448
55 #define HASHTABLE_BLOCKSIZE 7
56 #define HASHTABLE_FREE      3
57 #define ALLOCATION_TABLE_OFFSET 0x250
58 #define ALLOCATION_TABLE_SIZE   (0x1000 - ALLOCATION_TABLE_OFFSET)
59 #define HASHTABLE_NUM_ENTRIES   (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
60
61 #define DWORD_SIG(a,b,c,d)  (a | (b << 8) | (c << 16) | (d << 24))
62 #define URL_SIGNATURE   DWORD_SIG('U','R','L',' ')
63 #define REDR_SIGNATURE  DWORD_SIG('R','E','D','R')
64 #define LEAK_SIGNATURE  DWORD_SIG('L','E','A','K')
65 #define HASH_SIGNATURE  DWORD_SIG('H','A','S','H')
66
67 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
68
69 typedef struct _CACHEFILE_ENTRY
70 {
71 /*  union
72     {*/
73         DWORD dwSignature; /* e.g. "URL " */
74 /*      CHAR szSignature[4];
75     };*/
76     DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
77 } CACHEFILE_ENTRY;
78
79 typedef struct _URL_CACHEFILE_ENTRY
80 {
81     CACHEFILE_ENTRY CacheFileEntry;
82     FILETIME LastModifiedTime;
83     FILETIME LastAccessTime;
84     WORD wExpiredDate; /* expire date in dos format */
85     WORD wExpiredTime; /* expire time in dos format */
86     DWORD dwUnknown1; /* usually zero */
87     DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
88     DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
89     DWORD dwUnknown2; /* usually zero */
90     DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
91     DWORD dwUnknown3; /* usually 0x60 */
92     DWORD dwOffsetUrl; /* usually 0x68 */
93     BYTE CacheDir; /* index of cache directory this url is stored in */
94     BYTE Unknown4; /* usually zero */
95     WORD wUnknown5; /* usually 0x1010 */
96     DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
97     DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
98     DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
99     DWORD dwHeaderInfoSize;
100     DWORD dwUnknown6; /* usually zero */
101     WORD wLastSyncDate; /* last sync date in dos format */
102     WORD wLastSyncTime; /* last sync time in dos format */
103     DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
104     DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
105     WORD wUnknownDate; /* usually same as wLastSyncDate */
106     WORD wUnknownTime; /* usually same as wLastSyncTime */
107     DWORD dwUnknown7; /* usually zero */
108     DWORD dwUnknown8; /* usually zero */
109     CHAR szSourceUrlName[1]; /* start of url */
110     /* packing to dword align start of next field */
111     /* CHAR szLocalFileName[]; (local file name exluding path) */
112     /* packing to dword align start of next field */
113     /* CHAR szHeaderInfo[]; (header info) */
114 } URL_CACHEFILE_ENTRY;
115
116 struct _HASH_ENTRY
117 {
118     DWORD dwHashKey;
119     DWORD dwOffsetEntry;
120 };
121
122 typedef struct _HASH_CACHEFILE_ENTRY
123 {
124     CACHEFILE_ENTRY CacheFileEntry;
125     DWORD dwAddressNext;
126     DWORD dwHashTableNumber;
127     struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
128 } HASH_CACHEFILE_ENTRY;
129
130 typedef struct _DIRECTORY_DATA
131 {
132     DWORD dwUnknown;
133     char filename[DIR_LENGTH];
134 } DIRECTORY_DATA;
135
136 typedef struct _URLCACHE_HEADER
137 {
138     char szSignature[28];
139     DWORD dwFileSize;
140     DWORD dwOffsetFirstHashTable;
141     DWORD dwIndexCapacityInBlocks;
142     DWORD dwBlocksInUse; /* is this right? */
143     DWORD dwUnknown1;
144     DWORD dwCacheLimitLow; /* disk space limit for cache */
145     DWORD dwCacheLimitHigh; /* disk space limit for cache */
146     DWORD dwUnknown4; /* current disk space usage for cache? */
147     DWORD dwUnknown5; /* current disk space usage for cache? */
148     DWORD dwUnknown6; /* possibly a flag? */
149     DWORD dwUnknown7;
150     BYTE DirectoryCount; /* number of directory_data's */
151     BYTE Unknown8[3]; /* just padding? */
152     DIRECTORY_DATA directory_data[1]; /* first directory entry */
153 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
154 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
155
156 typedef struct _STREAM_HANDLE
157 {
158     HANDLE hFile;
159     CHAR lpszUrl[1];
160 } STREAM_HANDLE;
161
162 typedef struct _URLCACHECONTAINER
163 {
164     struct list entry; /* part of a list */
165     LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
166     LPWSTR path; /* path to url container directory */
167     HANDLE hMapping; /* handle of file mapping */
168     DWORD file_size; /* size of file when mapping was opened */
169     HANDLE hMutex; /* hande of mutex */
170 } URLCACHECONTAINER;
171
172
173 /* List of all containers available */
174 static struct list UrlContainers = LIST_INIT(UrlContainers);
175
176
177 /***********************************************************************
178  *           URLCache_PathToObjectName (Internal)
179  *
180  *  Converts a path to a name suitable for use as a Win32 object name.
181  * Replaces '\\' characters in-place with the specified character
182  * (usually '_' or '!')
183  *
184  * RETURNS
185  *    nothing
186  *
187  */
188 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
189 {
190     for (; *lpszPath; lpszPath++)
191     {
192         if (*lpszPath == '\\')
193             *lpszPath = replace;
194     }
195 }
196
197 /***********************************************************************
198  *           URLCacheContainer_OpenIndex (Internal)
199  *
200  *  Opens the index file and saves mapping handle in hCacheIndexMapping
201  *
202  * RETURNS
203  *    TRUE if succeeded
204  *    FALSE if failed
205  *
206  */
207 static BOOL URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
208 {
209     HANDLE hFile;
210     WCHAR wszFilePath[MAX_PATH];
211     DWORD dwFileSize;
212
213     static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
214     static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
215
216     if (pContainer->hMapping)
217         return TRUE;
218
219     strcpyW(wszFilePath, pContainer->path);
220     strcatW(wszFilePath, wszIndex);
221
222     hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
223     if (hFile == INVALID_HANDLE_VALUE)
224     {
225         FIXME("need to create cache index file\n");
226         return FALSE;
227     }
228
229     dwFileSize = GetFileSize(hFile, NULL);
230     if (dwFileSize == INVALID_FILE_SIZE)
231         return FALSE;
232
233     if (dwFileSize == 0)
234     {
235         FIXME("need to create cache index file\n");
236         return FALSE;
237     }
238
239     wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
240     URLCache_PathToObjectName(wszFilePath, '_');
241     pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
242     if (!pContainer->hMapping)
243         pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
244     CloseHandle(hFile);
245     if (!pContainer->hMapping)
246     {
247         ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
248         return FALSE;
249     }
250
251     return TRUE;
252 }
253
254 /***********************************************************************
255  *           URLCacheContainer_CloseIndex (Internal)
256  *
257  *  Closes the index
258  *
259  * RETURNS
260  *    nothing
261  *
262  */
263 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
264 {
265     CloseHandle(pContainer->hMapping);
266     pContainer->hMapping = NULL;
267 }
268
269 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
270 {
271     URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
272     int path_len = strlenW(path);
273     int cache_prefix_len = strlenW(cache_prefix);
274
275     if (!pContainer)
276     {
277         return FALSE;
278     }
279
280     pContainer->hMapping = NULL;
281     pContainer->file_size = 0;
282
283     pContainer->path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (path_len + 1) * sizeof(WCHAR));
284     if (!pContainer->path)
285     {
286         HeapFree(GetProcessHeap(), 0, pContainer);
287         return FALSE;
288     }
289
290     memcpy(pContainer->path, path, (path_len + 1) * sizeof(WCHAR));
291
292     pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
293     if (!pContainer->cache_prefix)
294     {
295         HeapFree(GetProcessHeap(), 0, pContainer->path);
296         HeapFree(GetProcessHeap(), 0, pContainer);
297         return FALSE;
298     }
299
300     memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
301
302     CharLowerW(mutex_name);
303     URLCache_PathToObjectName(mutex_name, '!');
304
305     if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
306     {
307         ERR("couldn't create mutex (error is %ld)\n", GetLastError());
308         HeapFree(GetProcessHeap(), 0, pContainer->path);
309         HeapFree(GetProcessHeap(), 0, pContainer);
310         return FALSE;
311     }
312
313     list_add_head(&UrlContainers, &pContainer->entry);
314
315     return TRUE;
316 }
317
318 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
319 {
320     list_remove(&pContainer->entry);
321
322     URLCacheContainer_CloseIndex(pContainer);
323     CloseHandle(pContainer->hMutex);
324     HeapFree(GetProcessHeap(), 0, pContainer->path);
325     HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
326     HeapFree(GetProcessHeap(), 0, pContainer);
327 }
328
329 void URLCacheContainers_CreateDefaults()
330 {
331     static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
332     static const WCHAR UrlPrefix[] = {0};
333     static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
334     static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
335     static const WCHAR CookieSuffix[] = {0};
336     static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
337     static const struct
338     {
339         int nFolder; /* CSIDL_* constant */
340         const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
341         const WCHAR * cache_prefix; /* prefix used to reference the container */
342     } DefaultContainerData[] = 
343     {
344         { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
345         { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
346         { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
347     };
348     int i;
349
350     for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
351     {
352         WCHAR wszCachePath[MAX_PATH];
353         WCHAR wszMutexName[MAX_PATH];
354         int path_len, suffix_len;
355
356         if (FAILED(SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE)))
357         {
358             ERR("Couldn't get path for default container %d\n", i);
359             continue;
360         }
361         path_len = strlenW(wszCachePath);
362         suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
363
364         if (path_len + suffix_len + 2 > MAX_PATH)
365         {
366             ERR("Path too long\n");
367             continue;
368         }
369
370         wszCachePath[path_len] = '\\';
371
372         strcpyW(wszMutexName, wszCachePath);
373         
374         if (suffix_len)
375         {
376             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
377             wszCachePath[path_len + suffix_len + 1] = '\\';
378             wszCachePath[path_len + suffix_len + 2] = '\0';
379         }
380
381         URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
382     }
383 }
384
385 void URLCacheContainers_DeleteAll()
386 {
387     while(!list_empty(&UrlContainers))
388         URLCacheContainer_DeleteContainer(
389             LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
390         );
391 }
392
393 static BOOL URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
394 {
395     struct list * cursor;
396
397     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
398
399     LIST_FOR_EACH(cursor, &UrlContainers)
400     {
401         URLCACHECONTAINER * pContainer = LIST_ENTRY(cursor, URLCACHECONTAINER, entry);
402         int prefix_len = strlenW(pContainer->cache_prefix);
403         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
404         {
405             TRACE("found container with prefx %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
406             *ppContainer = pContainer;
407             return TRUE;
408         }
409     }
410     ERR("no container found\n");
411     SetLastError(ERROR_FILE_NOT_FOUND);
412     return FALSE;
413 }
414
415 static BOOL URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
416 {
417     BOOL ret;
418     LPWSTR lpwszUrl;
419     int url_len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
420     if (url_len && (lpwszUrl = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(WCHAR))))
421     {
422         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, lpwszUrl, url_len);
423         ret = URLCacheContainers_FindContainerW(lpwszUrl, ppContainer);
424         HeapFree(GetProcessHeap(), 0, lpwszUrl);
425         return ret;
426     }
427     return FALSE;
428 }
429
430 /***********************************************************************
431  *           URLCacheContainer_LockIndex (Internal)
432  *
433  */
434 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
435 {
436     BYTE index;
437     LPVOID pIndexData;
438     URLCACHE_HEADER * pHeader;
439
440     /* acquire mutex */
441     WaitForSingleObject(pContainer->hMutex, INFINITE);
442
443     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
444
445     if (!pIndexData)
446     {
447         ReleaseMutex(pContainer->hMutex);
448         ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
449         return FALSE;
450     }
451     pHeader = (URLCACHE_HEADER *)pIndexData;
452
453     /* file has grown - we need to remap to prevent us getting
454      * access violations when we try and access beyond the end
455      * of the memory mapped file */
456     if (pHeader->dwFileSize != pContainer->file_size)
457     {
458         URLCacheContainer_CloseIndex(pContainer);
459         if (!URLCacheContainer_OpenIndex(pContainer))
460         {
461             ReleaseMutex(pContainer->hMutex);
462             return FALSE;
463         }
464         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
465
466         if (!pIndexData)
467         {
468             ReleaseMutex(pContainer->hMutex);
469             ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
470             return FALSE;
471         }
472         pHeader = (URLCACHE_HEADER *)pIndexData;
473     }
474
475     TRACE("Signature: %s, file size: %ld bytes\n", pHeader->szSignature, pHeader->dwFileSize);
476
477     for (index = 0; index < pHeader->DirectoryCount; index++)
478     {
479         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
480     }
481     
482     return pHeader;
483 }
484
485 /***********************************************************************
486  *           URLCacheContainer_UnlockIndex (Internal)
487  *
488  */
489 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
490 {
491     /* release mutex */
492     ReleaseMutex(pContainer->hMutex);
493     return UnmapViewOfFile(pHeader);
494 }
495
496
497 #ifndef CHAR_BIT
498 #define CHAR_BIT    (8 * sizeof(CHAR))
499 #endif
500
501 /***********************************************************************
502  *           URLCache_Allocation_BlockIsFree (Internal)
503  *
504  *  Is the specified block number free?
505  *
506  * RETURNS
507  *    zero if free
508  *    non-zero otherwise
509  *
510  */
511 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
512 {
513     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
514     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
515 }
516
517 /***********************************************************************
518  *           URLCache_Allocation_BlockFree (Internal)
519  *
520  *  Marks the specified block as free
521  *
522  * RETURNS
523  *    nothing
524  *
525  */
526 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
527 {
528     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
529     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
530 }
531
532 /***********************************************************************
533  *           URLCache_Allocation_BlockAllocate (Internal)
534  *
535  *  Marks the specified block as allocated
536  *
537  * RETURNS
538  *    nothing
539  *
540  */
541 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
542 {
543     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
544     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
545 }
546
547 /***********************************************************************
548  *           URLCache_FindFirstFreeEntry (Internal)
549  *
550  *  Finds and allocates the first block of free space big enough and
551  * sets ppEntry to point to it.
552  *
553  * RETURNS
554  *    TRUE if it had enough space
555  *    FALSE if it couldn't find enough space
556  *
557  */
558 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
559 {
560     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
561     DWORD dwBlockNumber;
562     DWORD dwFreeCounter;
563     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
564     {
565         for (dwFreeCounter = 0; 
566             dwFreeCounter < dwBlocksNeeded &&
567               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
568               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
569             dwFreeCounter++)
570                 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
571
572         if (dwFreeCounter == dwBlocksNeeded)
573         {
574             DWORD index;
575             TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
576             for (index = 0; index < dwBlocksNeeded; index++)
577                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
578             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
579             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
580             return TRUE;
581         }
582     }
583     FIXME("Grow file\n");
584     return FALSE;
585 }
586
587 /***********************************************************************
588  *           URLCache_DeleteEntry (Internal)
589  *
590  *  Deletes the specified entry and frees the space allocated to it
591  *
592  * RETURNS
593  *    TRUE if it succeeded
594  *    FALSE if it failed
595  *
596  */
597 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
598 {
599     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
600     return TRUE;
601 }
602
603 /***********************************************************************
604  *           URLCache_LocalFileNameToPathW (Internal)
605  *
606  *  Copies the full path to the specified buffer given the local file
607  * name and the index of the directory it is in. Always sets value in
608  * lpBufferSize to the required buffer size (in bytes).
609  *
610  * RETURNS
611  *    TRUE if the buffer was big enough
612  *    FALSE if the buffer was too small
613  *
614  */
615 static BOOL URLCache_LocalFileNameToPathW(
616     const URLCACHECONTAINER * pContainer,
617     LPCURLCACHE_HEADER pHeader,
618     LPCSTR szLocalFileName,
619     BYTE Directory,
620     LPWSTR wszPath,
621     LPLONG lpBufferSize)
622 {
623     LONG nRequired;
624     int path_len = strlenW(pContainer->path);
625     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
626     if (Directory >= pHeader->DirectoryCount)
627     {
628         *lpBufferSize = 0;
629         return FALSE;
630     }
631
632     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
633     if (nRequired < *lpBufferSize)
634     {
635         int dir_len;
636
637         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
638         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
639         wszPath[dir_len + path_len] = '\\';
640         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
641         *lpBufferSize = nRequired;
642         return TRUE;
643     }
644     *lpBufferSize = nRequired;
645     return FALSE;
646 }
647
648 /***********************************************************************
649  *           URLCache_LocalFileNameToPathA (Internal)
650  *
651  *  Copies the full path to the specified buffer given the local file
652  * name and the index of the directory it is in. Always sets value in
653  * lpBufferSize to the required buffer size.
654  *
655  * RETURNS
656  *    TRUE if the buffer was big enough
657  *    FALSE if the buffer was too small
658  *
659  */
660 static BOOL URLCache_LocalFileNameToPathA(
661     const URLCACHECONTAINER * pContainer,
662     LPCURLCACHE_HEADER pHeader,
663     LPCSTR szLocalFileName,
664     BYTE Directory,
665     LPSTR szPath,
666     LPLONG lpBufferSize)
667 {
668     LONG nRequired;
669     int path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL);
670     int file_name_len = strlen(szLocalFileName);
671     int dir_len = DIR_LENGTH;
672     if (Directory >= pHeader->DirectoryCount)
673     {
674         *lpBufferSize = 0;
675         return FALSE;
676     }
677
678     nRequired = (path_len + dir_len + file_name_len + 1) * sizeof(WCHAR);
679     if (nRequired < *lpBufferSize)
680     {
681         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, -1, NULL, NULL);
682         strncpy(szPath, pHeader->directory_data[Directory].filename, DIR_LENGTH);
683         szPath[dir_len + path_len] = '\\';
684         strcpy(szPath + dir_len + path_len + 1, szLocalFileName);
685         *lpBufferSize = nRequired;
686         return TRUE;
687     }
688     *lpBufferSize = nRequired;
689     return FALSE;
690 }
691
692 /***********************************************************************
693  *           URLCache_CopyEntry (Internal)
694  *
695  *  Copies an entry from the cache index file to the Win32 structure
696  *
697  * RETURNS
698  *    TRUE if the buffer was big enough
699  *    FALSE if the buffer was too small
700  *
701  */
702 static BOOL URLCache_CopyEntry(
703     URLCACHECONTAINER * pContainer,
704     LPCURLCACHE_HEADER pHeader, 
705     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
706     LPDWORD lpdwBufferSize, 
707     URL_CACHEFILE_ENTRY * pUrlEntry,
708     BOOL bUnicode)
709 {
710     int lenUrl;
711     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
712
713     if (*lpdwBufferSize >= dwRequiredSize)
714     {
715         lpCacheEntryInfo->lpHeaderInfo = NULL;
716         lpCacheEntryInfo->lpszFileExtension = NULL;
717         lpCacheEntryInfo->lpszLocalFileName = NULL;
718         lpCacheEntryInfo->lpszSourceUrlName = NULL;
719         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
720         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
721         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
722         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
723         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
724         lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
725         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
726         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
727         DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
728         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
729         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
730         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
731         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
732         DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
733     }
734
735     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
736         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
737     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
738     if (bUnicode)
739         lenUrl = MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, NULL, 0);
740     else
741         lenUrl = strlen(pUrlEntry->szSourceUrlName);
742     dwRequiredSize += lenUrl + 1;
743     
744     /* FIXME: is source url optional? */
745     if (*lpdwBufferSize >= dwRequiredSize)
746     {
747         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
748         if (bUnicode)
749             MultiByteToWideChar(CP_ACP, 0, pUrlEntry->szSourceUrlName, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
750         else
751             memcpy(lpCacheEntryInfo->lpszSourceUrlName, pUrlEntry->szSourceUrlName, (lenUrl + 1) * sizeof(CHAR));
752     }
753
754     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
755         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
756     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
757
758     if (pUrlEntry->dwOffsetLocalName)
759     {
760         LONG nLocalFilePathSize;
761         LPSTR lpszLocalFileName;
762         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
763         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
764         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
765             URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
766         {
767             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
768         }
769         dwRequiredSize += nLocalFilePathSize;
770
771         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
772             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
773         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
774     }
775     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
776
777     if (*lpdwBufferSize >= dwRequiredSize)
778     {
779         lpCacheEntryInfo->lpHeaderInfo = (LPSTR)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
780         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
781         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
782     }
783     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
784         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
785     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
786
787     if (dwRequiredSize > *lpdwBufferSize)
788     {
789         *lpdwBufferSize = dwRequiredSize;
790         SetLastError(ERROR_INSUFFICIENT_BUFFER);
791         return FALSE;
792     }
793     *lpdwBufferSize = dwRequiredSize;
794     return TRUE;
795 }
796
797
798 /***********************************************************************
799  *           URLCache_SetEntryInfo (Internal)
800  *
801  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
802  * according the the flags set by dwFieldControl.
803  *
804  * RETURNS
805  *    TRUE if the buffer was big enough
806  *    FALSE if the buffer was too small
807  *
808  */
809 static BOOL URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
810 {
811     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
812         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
813     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
814         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
815     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
816         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
817     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
818         FIXME("CACHE_ENTRY_EXPTIME_FC unimplemented\n");
819     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
820         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
821     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
822         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
823     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
824         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
825     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
826         FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
827
828     return TRUE;
829 }
830
831 /***********************************************************************
832  *           URLCache_HashKey (Internal)
833  *
834  *  Returns the hash key for a given string
835  *
836  * RETURNS
837  *    hash key for the string
838  *
839  */
840 static DWORD URLCache_HashKey(LPCSTR lpszKey)
841 {
842     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
843      * but the algorithm and result are not the same!
844      */
845     static const unsigned char lookupTable[256] = 
846     {
847         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
848         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
849         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
850         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
851         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
852         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
853         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
854         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
855         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
856         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
857         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
858         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
859         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
860         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
861         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
862         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
863         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
864         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
865         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
866         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
867         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
868         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
869         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
870         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
871         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
872         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
873         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
874         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
875         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
876         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
877         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
878         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
879     };
880     BYTE key[4];
881     int i;
882     int subscript[sizeof(key) / sizeof(key[0])];
883
884     subscript[0] = *lpszKey;
885     subscript[1] = (char)(*lpszKey + 1);
886     subscript[2] = (char)(*lpszKey + 2);
887     subscript[3] = (char)(*lpszKey + 3);
888
889     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
890         key[i] = lookupTable[i];
891
892     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
893     {
894         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
895             key[i] = lookupTable[*lpszKey ^ key[i]];
896     }
897
898     return *(DWORD *)key;
899 }
900
901 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
902 {
903     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
904 }
905
906 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
907 {
908     /* structure of hash table:
909      *  448 entries divided into 64 blocks
910      *  each block therefore contains a chain of 7 key/offset pairs
911      * how position in table is calculated:
912      *  1. the url is hashed in helper function
913      *  2. the key % 64 * 8 is the offset
914      *  3. the key in the hash table is the hash key aligned to 64
915      *
916      * note:
917      *  there can be multiple hash tables in the file and the offset to
918      *  the next one is stored in the header of the hash table
919      */
920     DWORD key = URLCache_HashKey(lpszUrl);
921     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
922     HASH_CACHEFILE_ENTRY * pHashEntry;
923     DWORD dwHashTableNumber = 0;
924
925     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
926
927     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
928          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
929          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
930     {
931         int i;
932         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
933         {
934             ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
935             continue;
936         }
937         /* make sure that it is in fact a hash entry */
938         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
939         {
940             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
941             continue;
942         }
943
944         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
945         {
946             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
947             if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
948             {
949                 /* FIXME: we should make sure that this is the right element
950                  * before returning and claiming that it is. We can do this
951                  * by doing a simple compare between the URL we were given
952                  * and the URL stored in the entry. However, this assumes
953                  * we know the format of all the entries stored in the
954                  * hash table */
955                 *ppHashEntry = pHashElement;
956                 return TRUE;
957             }
958         }
959     }
960     return FALSE;
961 }
962
963 /***********************************************************************
964  *           URLCache_FindEntryInHash (Internal)
965  *
966  *  Searches all the hash tables in the index for the given URL and
967  * returns the entry, if it was found, in ppEntry
968  *
969  * RETURNS
970  *    TRUE if the entry was found
971  *    FALSE if the entry could not be found
972  *
973  */
974 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
975 {
976     struct _HASH_ENTRY * pHashEntry;
977     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
978     {
979         *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
980         return TRUE;
981     }
982     return FALSE;
983 }
984
985 /***********************************************************************
986  *           URLCache_HashEntrySetUse (Internal)
987  *
988  *  Searches all the hash tables in the index for the given URL and
989  * sets the use count (stored or'ed with key)
990  *
991  * RETURNS
992  *    TRUE if the entry was found
993  *    FALSE if the entry could not be found
994  *
995  */
996 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
997 {
998     struct _HASH_ENTRY * pHashEntry;
999     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1000     {
1001         pHashEntry->dwHashKey = dwUseCount | (DWORD)(pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1002         return TRUE;
1003     }
1004     return FALSE;
1005 }
1006
1007 /***********************************************************************
1008  *           URLCache_DeleteEntryFromHash (Internal)
1009  *
1010  *  Searches all the hash tables in the index for the given URL and
1011  * then if found deletes the entry.
1012  *
1013  * RETURNS
1014  *    TRUE if the entry was found
1015  *    FALSE if the entry could not be found
1016  *
1017  */
1018 static BOOL URLCache_DeleteEntryFromHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl)
1019 {
1020     struct _HASH_ENTRY * pHashEntry;
1021     if (URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1022     {
1023         pHashEntry->dwHashKey = HASHTABLE_FREE;
1024         pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1025         return TRUE;
1026     }
1027     return FALSE;
1028 }
1029
1030 /***********************************************************************
1031  *           URLCache_AddEntryToHash (Internal)
1032  *
1033  *  Searches all the hash tables for a free slot based on the offset
1034  * generated from the hash key. If a free slot is found, the offset and
1035  * key are entered into the hash table.
1036  *
1037  * RETURNS
1038  *    TRUE if the entry was added
1039  *    FALSE if the entry could not be added
1040  *
1041  */
1042 static BOOL URLCache_AddEntryToHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1043 {
1044     /* see URLCache_FindEntryInHash for structure of hash tables */
1045
1046     DWORD key = URLCache_HashKey(lpszUrl);
1047     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1048     HASH_CACHEFILE_ENTRY * pHashEntry;
1049     DWORD dwHashTableNumber = 0;
1050
1051     key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1052
1053     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1054          ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
1055          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1056     {
1057         int i;
1058         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1059         {
1060             ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1061             break;
1062         }
1063         /* make sure that it is in fact a hash entry */
1064         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1065         {
1066             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1067             break;
1068         }
1069
1070         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1071         {
1072             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1073             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1074             {
1075                 pHashElement->dwHashKey = key;
1076                 pHashElement->dwOffsetEntry = dwOffsetEntry;
1077                 return TRUE;
1078             }
1079         }
1080     }
1081     FIXME("need to create another hash table\n");
1082     return FALSE;
1083 }
1084
1085 /***********************************************************************
1086  *           GetUrlCacheEntryInfoExA (WININET.@)
1087  *
1088  */
1089 BOOL WINAPI GetUrlCacheEntryInfoExA(
1090     LPCSTR lpszUrl,
1091     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1092     LPDWORD lpdwCacheEntryInfoBufSize,
1093     LPSTR lpszReserved,
1094     LPDWORD lpdwReserved,
1095     LPVOID lpReserved,
1096     DWORD dwFlags)
1097 {
1098     TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1099         debugstr_a(lpszUrl), 
1100         lpCacheEntryInfo,
1101         lpdwCacheEntryInfoBufSize,
1102         lpszReserved,
1103         lpdwReserved,
1104         lpReserved,
1105         dwFlags);
1106
1107     if ((lpszReserved != NULL) ||
1108         (lpdwReserved != NULL) ||
1109         (lpReserved != NULL))
1110     {
1111         ERR("Reserved value was not 0\n");
1112         SetLastError(ERROR_INVALID_PARAMETER);
1113         return FALSE;
1114     }
1115     if (dwFlags != 0)
1116         FIXME("Undocumented flag(s): %lx\n", dwFlags);
1117     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1118 }
1119
1120 /***********************************************************************
1121  *           GetUrlCacheEntryInfoA (WININET.@)
1122  *
1123  */
1124 BOOL WINAPI GetUrlCacheEntryInfoA(
1125     IN LPCSTR lpszUrlName,
1126     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1127     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1128 )
1129 {
1130     LPURLCACHE_HEADER pHeader;
1131     CACHEFILE_ENTRY * pEntry;
1132     URL_CACHEFILE_ENTRY * pUrlEntry;
1133     URLCACHECONTAINER * pContainer;
1134
1135     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1136
1137     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1138         return FALSE;
1139
1140     if (!URLCacheContainer_OpenIndex(pContainer))
1141         return FALSE;
1142
1143     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1144         return FALSE;
1145
1146     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1147     {
1148         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1149         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1150         SetLastError(ERROR_FILE_NOT_FOUND);
1151         return FALSE;
1152     }
1153
1154     if (pEntry->dwSignature != URL_SIGNATURE)
1155     {
1156         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1157         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1158         SetLastError(ERROR_FILE_NOT_FOUND);
1159         return FALSE;
1160     }
1161
1162     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1163     TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1164     if (pUrlEntry->dwOffsetHeaderInfo)
1165         TRACE("Header info: %s\n", debugstr_a((LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1166
1167     if (!URLCache_CopyEntry(
1168         pContainer,
1169         pHeader,
1170         lpCacheEntryInfo,
1171         lpdwCacheEntryInfoBufferSize,
1172         pUrlEntry,
1173         FALSE /* ANSI */))
1174     {
1175         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1176         return FALSE;
1177     }
1178     TRACE("Local File Name: %s\n", debugstr_a(lpCacheEntryInfo->lpszLocalFileName));
1179
1180     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1181
1182     return TRUE;
1183 }
1184
1185 /***********************************************************************
1186  *           GetUrlCacheEntryInfoW (WININET.@)
1187  *
1188  */
1189 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1190   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1191   LPDWORD lpdwCacheEntryInfoBufferSize)
1192 {
1193     LPURLCACHE_HEADER pHeader;
1194     CACHEFILE_ENTRY * pEntry;
1195     URL_CACHEFILE_ENTRY * pUrlEntry;
1196     URLCACHECONTAINER * pContainer;
1197     LPSTR lpszUrlA;
1198     int url_len;
1199
1200     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1201
1202     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1203     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1204     if (!lpszUrlA)
1205     {
1206         SetLastError(ERROR_OUTOFMEMORY);
1207         return FALSE;
1208     }
1209     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1210
1211     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1212     {
1213         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1214         return FALSE;
1215     }
1216
1217     if (!URLCacheContainer_OpenIndex(pContainer))
1218     {
1219         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1220         return FALSE;
1221     }
1222
1223     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1224     {
1225         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1226         return FALSE;
1227     }
1228
1229     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1230     {
1231         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1232         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1233         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1234         SetLastError(ERROR_FILE_NOT_FOUND);
1235         return FALSE;
1236     }
1237     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1238
1239     if (pEntry->dwSignature != URL_SIGNATURE)
1240     {
1241         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1242         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1243         SetLastError(ERROR_FILE_NOT_FOUND);
1244         return FALSE;
1245     }
1246
1247     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1248     TRACE("Found URL: %s\n", debugstr_a(pUrlEntry->szSourceUrlName));
1249     TRACE("Header info: %s\n", debugstr_a((LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1250
1251     if (!URLCache_CopyEntry(
1252         pContainer,
1253         pHeader,
1254         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1255         lpdwCacheEntryInfoBufferSize,
1256         pUrlEntry,
1257         TRUE /* UNICODE */))
1258     {
1259         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1260         return FALSE;
1261     }
1262     TRACE("Local File Name: %s\n", debugstr_w(lpCacheEntryInfo->lpszLocalFileName));
1263
1264     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1265
1266     return TRUE;
1267 }
1268
1269 /***********************************************************************
1270  *           GetUrlCacheEntryInfoExW (WININET.@)
1271  *
1272  */
1273 BOOL WINAPI GetUrlCacheEntryInfoExW(
1274     LPCWSTR lpszUrl,
1275     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1276     LPDWORD lpdwCacheEntryInfoBufSize,
1277     LPWSTR lpszReserved,
1278     LPDWORD lpdwReserved,
1279     LPVOID lpReserved,
1280     DWORD dwFlags)
1281 {
1282     TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
1283         debugstr_w(lpszUrl), 
1284         lpCacheEntryInfo,
1285         lpdwCacheEntryInfoBufSize,
1286         lpszReserved,
1287         lpdwReserved,
1288         lpReserved,
1289         dwFlags);
1290
1291     if ((lpszReserved != NULL) ||
1292         (lpdwReserved != NULL) ||
1293         (lpReserved != NULL))
1294     {
1295         ERR("Reserved value was not 0\n");
1296         SetLastError(ERROR_INVALID_PARAMETER);
1297         return FALSE;
1298     }
1299     if (dwFlags != 0)
1300         FIXME("Undocumented flag(s): %lx\n", dwFlags);
1301     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1302 }
1303
1304 /***********************************************************************
1305  *           SetUrlCacheEntryInfoA (WININET.@)
1306  */
1307 BOOL WINAPI SetUrlCacheEntryInfoA(
1308     LPCSTR lpszUrlName,
1309     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1310     DWORD dwFieldControl)
1311 {
1312     LPURLCACHE_HEADER pHeader;
1313     CACHEFILE_ENTRY * pEntry;
1314     URLCACHECONTAINER * pContainer;
1315
1316     TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1317
1318     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1319         return FALSE;
1320
1321     if (!URLCacheContainer_OpenIndex(pContainer))
1322         return FALSE;
1323
1324     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1325         return FALSE;
1326
1327     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1328     {
1329         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1330         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1331         SetLastError(ERROR_FILE_NOT_FOUND);
1332         return FALSE;
1333     }
1334
1335     if (pEntry->dwSignature != URL_SIGNATURE)
1336     {
1337         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1338         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1339         SetLastError(ERROR_FILE_NOT_FOUND);
1340         return FALSE;
1341     }
1342
1343     URLCache_SetEntryInfo(
1344         (URL_CACHEFILE_ENTRY *)pEntry,
1345         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1346         dwFieldControl);
1347
1348     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1349
1350     return TRUE;
1351 }
1352
1353 /***********************************************************************
1354  *           SetUrlCacheEntryInfoW (WININET.@)
1355  */
1356 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1357 {
1358     LPURLCACHE_HEADER pHeader;
1359     CACHEFILE_ENTRY * pEntry;
1360     URLCACHECONTAINER * pContainer;
1361     LPSTR lpszUrlA;
1362     int url_len;
1363
1364     TRACE("(%s, %p, 0x%08lx)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1365
1366     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1367     lpszUrlA = HeapAlloc(GetProcessHeap(), 0, url_len * sizeof(CHAR));
1368     if (!lpszUrlA)
1369     {
1370         SetLastError(ERROR_OUTOFMEMORY);
1371         return FALSE;
1372     }
1373     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, lpszUrlA, url_len, NULL, NULL);
1374
1375     if (!URLCacheContainers_FindContainerW(lpszUrl, &pContainer))
1376     {
1377         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1378         return FALSE;
1379     }
1380
1381     if (!URLCacheContainer_OpenIndex(pContainer))
1382     {
1383         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1384         return FALSE;
1385     }
1386
1387     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1388     {
1389         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1390         return FALSE;
1391     }
1392
1393     if (!URLCache_FindEntryInHash(pHeader, lpszUrlA, &pEntry))
1394     {
1395         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1396         HeapFree(GetProcessHeap(), 0, lpszUrlA);
1397         WARN("entry %s not found!\n", debugstr_a(lpszUrlA));
1398         SetLastError(ERROR_FILE_NOT_FOUND);
1399         return FALSE;
1400     }
1401     HeapFree(GetProcessHeap(), 0, lpszUrlA);
1402
1403     if (pEntry->dwSignature != URL_SIGNATURE)
1404     {
1405         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1406         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1407         SetLastError(ERROR_FILE_NOT_FOUND);
1408         return FALSE;
1409     }
1410
1411     URLCache_SetEntryInfo(
1412         (URL_CACHEFILE_ENTRY *)pEntry,
1413         lpCacheEntryInfo,
1414         dwFieldControl);
1415
1416     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1417
1418     return TRUE;
1419 }
1420
1421 /***********************************************************************
1422  *           RetrieveUrlCacheEntryFileA (WININET.@)
1423  *
1424  */
1425 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1426     IN LPCSTR lpszUrlName,
1427     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
1428     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1429     IN DWORD dwReserved
1430     )
1431 {
1432     LPURLCACHE_HEADER pHeader;
1433     CACHEFILE_ENTRY * pEntry;
1434     URL_CACHEFILE_ENTRY * pUrlEntry;
1435     URLCACHECONTAINER * pContainer;
1436
1437     TRACE("(%s, %p, %p, 0x%08lx)\n",
1438         debugstr_a(lpszUrlName),
1439         lpCacheEntryInfo,
1440         lpdwCacheEntryInfoBufferSize,
1441         dwReserved);
1442
1443     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1444         return FALSE;
1445
1446     if (!URLCacheContainer_OpenIndex(pContainer))
1447         return FALSE;
1448
1449     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1450         return FALSE;
1451
1452     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1453     {
1454         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1455         TRACE("entry %s not found!\n", lpszUrlName);
1456         SetLastError(ERROR_FILE_NOT_FOUND);
1457         return FALSE;
1458     }
1459
1460     if (pEntry->dwSignature != URL_SIGNATURE)
1461     {
1462         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1463         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1464         SetLastError(ERROR_FILE_NOT_FOUND);
1465         return FALSE;
1466     }
1467
1468     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1469     TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
1470     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1471
1472     pUrlEntry->dwHitRate++;
1473     pUrlEntry->dwUseCount++;
1474     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1475
1476     if (!URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry, FALSE))
1477     {
1478         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1479         return FALSE;
1480     }
1481     TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
1482
1483     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1484
1485     return TRUE;
1486 }
1487
1488 /***********************************************************************
1489  *           UnlockUrlCacheEntryFileA (WININET.@)
1490  *
1491  */
1492 BOOL WINAPI UnlockUrlCacheEntryFileA(
1493     IN LPCSTR lpszUrlName, 
1494     IN DWORD dwReserved
1495     )
1496 {
1497     LPURLCACHE_HEADER pHeader;
1498     CACHEFILE_ENTRY * pEntry;
1499     URL_CACHEFILE_ENTRY * pUrlEntry;
1500     URLCACHECONTAINER * pContainer;
1501
1502     TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName), dwReserved);
1503
1504     if (dwReserved)
1505     {
1506         ERR("dwReserved != 0\n");
1507         SetLastError(ERROR_INVALID_PARAMETER);
1508         return FALSE;
1509     }
1510
1511     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1512        return FALSE;
1513
1514     if (!URLCacheContainer_OpenIndex(pContainer))
1515         return FALSE;
1516
1517     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1518         return FALSE;
1519
1520     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1521     {
1522         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1523         TRACE("entry %s not found!\n", lpszUrlName);
1524         SetLastError(ERROR_FILE_NOT_FOUND);
1525         return FALSE;
1526     }
1527
1528     if (pEntry->dwSignature != URL_SIGNATURE)
1529     {
1530         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1531         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1532         SetLastError(ERROR_FILE_NOT_FOUND);
1533         return FALSE;
1534     }
1535
1536     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1537
1538     if (pUrlEntry->dwUseCount == 0)
1539     {
1540         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1541         return FALSE;
1542     }
1543     pUrlEntry->dwUseCount--;
1544     URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1545
1546     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1547
1548     return TRUE;
1549 }
1550
1551 /***********************************************************************
1552  *           CreateUrlCacheEntryA (WININET.@)
1553  *
1554  */
1555 BOOL WINAPI CreateUrlCacheEntryA(
1556     IN LPCSTR lpszUrlName,
1557     IN DWORD dwExpectedFileSize,
1558     IN LPCSTR lpszFileExtension,
1559     OUT LPSTR lpszFileName,
1560     IN DWORD dwReserved
1561 )
1562 {
1563     URLCACHECONTAINER * pContainer;
1564     LPURLCACHE_HEADER pHeader;
1565     CHAR szFile[MAX_PATH];
1566     CHAR szExtension[MAX_PATH];
1567     LPCSTR lpszUrlPart;
1568     LPCSTR lpszUrlEnd;
1569     LPCSTR lpszFileNameExtension;
1570     LPSTR lpszFileNameNoPath;
1571     int i;
1572     int countnoextension;
1573     BYTE CacheDir;
1574     LONG lBufferSize;
1575     BOOL bFound = FALSE;
1576     int count;
1577
1578     TRACE("(%s, 0x%08lx, %s, %p, 0x%08lx)\n",
1579         debugstr_a(lpszUrlName),
1580         dwExpectedFileSize,
1581         debugstr_a(lpszFileExtension),
1582         lpszFileName,
1583         dwReserved);
1584
1585     if (dwReserved)
1586     {
1587         ERR("dwReserved != 0\n");
1588         SetLastError(ERROR_INVALID_PARAMETER);
1589         return FALSE;
1590     }
1591
1592     for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1593         ;
1594     
1595     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1596         lpszUrlEnd--;
1597
1598     for (lpszUrlPart = lpszUrlEnd; 
1599         (lpszUrlPart >= lpszUrlName); 
1600         lpszUrlPart--)
1601     {
1602         if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1603         {
1604             bFound = TRUE;
1605             lpszUrlPart++;
1606             break;
1607         }
1608     }
1609     if (!strcmp(lpszUrlPart, "www"))
1610     {
1611         lpszUrlPart += strlen("www");
1612     }
1613
1614     count = lpszUrlEnd - lpszUrlPart;
1615
1616     if (bFound && (count < MAX_PATH))
1617     {
1618         memcpy(szFile, lpszUrlPart, count * sizeof(CHAR));
1619         szFile[count] = '\0';
1620         /* FIXME: get rid of illegal characters like \, / and : */
1621     }
1622     else
1623     {
1624         FIXME("need to generate a random filename");
1625     }
1626
1627     TRACE("File name: %s\n", szFile);
1628
1629     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1630         return FALSE;
1631
1632     if (!URLCacheContainer_OpenIndex(pContainer))
1633         return FALSE;
1634
1635     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1636         return FALSE;
1637
1638     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1639
1640     lBufferSize = MAX_PATH * sizeof(CHAR);
1641     URLCache_LocalFileNameToPathA(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1642
1643     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1644
1645     lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(CHAR) + DIR_LENGTH + 1;
1646
1647     countnoextension = strlen(lpszFileNameNoPath);
1648     lpszFileNameExtension = PathFindExtensionA(lpszFileNameNoPath);
1649     if (lpszFileNameExtension)
1650         countnoextension -= strlen(lpszFileNameExtension);
1651     *szExtension = '\0';
1652
1653     if (lpszFileExtension)
1654     {
1655         szExtension[0] = '.';
1656         strcpy(szExtension+1, lpszFileExtension);
1657     }
1658
1659     for (i = 0; i < 255; i++)
1660     {
1661         HANDLE hFile;
1662         strncpy(lpszFileNameNoPath, szFile, countnoextension);
1663         sprintf(lpszFileNameNoPath + countnoextension, "[%u]%s", i, szExtension);
1664         TRACE("Trying: %s\n", lpszFileName);
1665         hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1666         if (hFile != INVALID_HANDLE_VALUE)
1667         {
1668             CloseHandle(hFile);
1669             return TRUE;
1670         }
1671     }
1672
1673     return FALSE;
1674 }
1675
1676 /***********************************************************************
1677  *           CommitUrlCacheEntryA (WININET.@)
1678  *
1679  */
1680 BOOL WINAPI CommitUrlCacheEntryA(
1681     IN LPCSTR lpszUrlName,
1682     IN LPCSTR lpszLocalFileName,
1683     IN FILETIME ExpireTime,
1684     IN FILETIME LastModifiedTime,
1685     IN DWORD CacheEntryType,
1686     IN LPBYTE lpHeaderInfo,
1687     IN DWORD dwHeaderSize,
1688     IN LPCSTR lpszFileExtension,
1689     IN LPCSTR dwReserved
1690     )
1691 {
1692     URLCACHECONTAINER * pContainer;
1693     LPURLCACHE_HEADER pHeader;
1694     CACHEFILE_ENTRY * pEntry;
1695     URL_CACHEFILE_ENTRY * pUrlEntry;
1696     DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1697     DWORD dwOffsetLocalFileName = 0;
1698     DWORD dwOffsetHeader = 0;
1699     DWORD dwFileSizeLow = 0;
1700     DWORD dwFileSizeHigh = 0;
1701     BYTE cDirectory = 0;
1702
1703     TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1704         debugstr_a(lpszUrlName),
1705         debugstr_a(lpszLocalFileName),
1706         CacheEntryType,
1707         lpHeaderInfo,
1708         dwHeaderSize,
1709         lpszFileExtension,
1710         dwReserved);
1711
1712     if (dwReserved)
1713     {
1714         ERR("dwReserved != 0\n");
1715         SetLastError(ERROR_INVALID_PARAMETER);
1716         return FALSE;
1717     }
1718     if (lpHeaderInfo == NULL)
1719     {
1720         FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1721     }
1722  
1723     if (lpszLocalFileName)
1724     {
1725         HANDLE hFile;
1726         hFile = CreateFileA(lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1727         if (hFile == INVALID_HANDLE_VALUE)
1728         {
1729             ERR("couldn't open file %s (error is %ld)\n", debugstr_a(lpszLocalFileName), GetLastError());
1730             return FALSE;
1731         }
1732
1733         /* Get file size */
1734         dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1735         if ((dwFileSizeLow == -1) && (GetLastError() != NO_ERROR))
1736         {
1737             ERR("couldn't get file size (error is %ld)\n", GetLastError());
1738             CloseHandle(hFile);
1739             return FALSE;
1740         }
1741
1742         CloseHandle(hFile);
1743     }
1744
1745     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
1746         return FALSE;
1747
1748     if (!URLCacheContainer_OpenIndex(pContainer))
1749         return FALSE;
1750
1751     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1752         return FALSE;
1753
1754     if (URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1755     {
1756         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1757         FIXME("entry already in cache - don't know what to do!\n");
1758 /*
1759  *        SetLastError(ERROR_FILE_NOT_FOUND);
1760  *        return FALSE;
1761  */
1762         return TRUE;
1763     }
1764
1765     if (lpszLocalFileName)
1766     {
1767         BOOL bFound = FALSE;
1768         char szContainerPath[MAX_PATH];
1769         int container_path_len;
1770         container_path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szContainerPath, sizeof(szContainerPath), NULL, NULL);
1771         if (!container_path_len)
1772         {
1773             /* WideCharToMultiByte should have called SetLastError */
1774             return FALSE;
1775         }
1776
1777         if (strncmp(lpszLocalFileName, szContainerPath, container_path_len))
1778         {
1779             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1780             ERR("path %s must begin with cache content path %s\n", debugstr_a(lpszLocalFileName), debugstr_a(szContainerPath));
1781             SetLastError(ERROR_INVALID_PARAMETER);
1782             return FALSE;
1783         }
1784
1785         /* skip container path prefix */
1786         lpszLocalFileName += container_path_len;
1787
1788         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1789         {
1790             if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1791             {
1792                 bFound = TRUE;
1793                 break;
1794             }
1795         }
1796
1797         if (!bFound)
1798         {
1799             URLCacheContainer_UnlockIndex(pContainer, pHeader);
1800             ERR("cache directory not found in path %s\n", lpszLocalFileName);
1801             SetLastError(ERROR_INVALID_PARAMETER);
1802             return FALSE;
1803         }
1804
1805         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1806     }
1807
1808     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlName) + 1);
1809     if (lpszLocalFileName)
1810     {
1811         dwOffsetLocalFileName = dwBytesNeeded;
1812         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszLocalFileName) + 1);
1813     }
1814     if (lpHeaderInfo)
1815     {
1816         dwOffsetHeader = dwBytesNeeded;
1817         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1818     }
1819
1820     /* round up to next block */
1821     if (dwBytesNeeded % BLOCKSIZE)
1822     {
1823         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1824         dwBytesNeeded += BLOCKSIZE;
1825     }
1826
1827     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1828     {
1829         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1830         ERR("no free entries\n");
1831         return FALSE;
1832     }
1833
1834     /* FindFirstFreeEntry fills in blocks used */
1835     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1836     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1837     pUrlEntry->CacheDir = cDirectory;
1838     pUrlEntry->CacheEntryType = CacheEntryType;
1839     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1840     pUrlEntry->dwExemptDelta = 0;
1841     pUrlEntry->dwHitRate = 0;
1842     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1843     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1844     pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1845     pUrlEntry->dwSizeHigh = 0;
1846     pUrlEntry->dwSizeLow = dwFileSizeLow;
1847     pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1848     pUrlEntry->dwUseCount = 0;
1849     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1850     pUrlEntry->LastModifiedTime = LastModifiedTime;
1851     FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1852     FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1853     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1854     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1855
1856     /*** Unknowns ***/
1857     pUrlEntry->dwUnknown1 = 0;
1858     pUrlEntry->dwUnknown2 = 0;
1859     pUrlEntry->dwUnknown3 = 0x60;
1860     pUrlEntry->Unknown4 = 0;
1861     pUrlEntry->wUnknown5 = 0x1010;
1862     pUrlEntry->dwUnknown6 = 0;
1863     pUrlEntry->dwUnknown7 = 0;
1864     pUrlEntry->dwUnknown8 = 0;
1865
1866     strcpy(pUrlEntry->szSourceUrlName, lpszUrlName);
1867     if (dwOffsetLocalFileName)
1868         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), lpszLocalFileName);
1869     if (dwOffsetHeader)
1870         memcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetHeader), lpHeaderInfo, dwHeaderSize);
1871
1872     if (!URLCache_AddEntryToHash(pHeader, lpszUrlName, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1873     {
1874         URLCacheContainer_UnlockIndex(pContainer, pHeader);
1875         return FALSE;
1876     }
1877
1878     URLCacheContainer_UnlockIndex(pContainer, pHeader);
1879
1880     return TRUE;
1881 }
1882
1883 BOOL WINAPI ReadUrlCacheEntryStream(
1884     IN HANDLE hUrlCacheStream,
1885     IN  DWORD dwLocation,
1886     IN OUT LPVOID lpBuffer,
1887     IN OUT LPDWORD lpdwLen,
1888     IN DWORD dwReserved
1889     )
1890 {
1891     /* Get handle to file from 'stream' */
1892     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1893
1894     if (dwReserved != 0)
1895     {
1896         ERR("dwReserved != 0\n");
1897         SetLastError(ERROR_INVALID_PARAMETER);
1898         return FALSE;
1899     }
1900
1901     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1902     {
1903         SetLastError(ERROR_INVALID_HANDLE);
1904         return FALSE;
1905     }
1906
1907     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == -1)
1908         return FALSE;
1909     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
1910 }
1911
1912 /***********************************************************************
1913  *           RetrieveUrlCacheEntryStreamA (WININET.@)
1914  *
1915  */
1916 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
1917     IN LPCSTR lpszUrlName,
1918     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1919     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1920     IN BOOL fRandomRead,
1921     IN DWORD dwReserved
1922     )
1923 {
1924     /* NOTE: this is not the same as the way that the native
1925      * version allocates 'stream' handles. I did it this way
1926      * as it is much easier and no applications should depend
1927      * on this behaviour. (Native version appears to allocate
1928      * indices into a table)
1929      */
1930     STREAM_HANDLE * pStream;
1931     HANDLE hFile;
1932
1933     if (!RetrieveUrlCacheEntryFileA(lpszUrlName, 
1934         lpCacheEntryInfo, 
1935         lpdwCacheEntryInfoBufferSize,
1936         dwReserved))
1937     {
1938         return NULL;
1939     }
1940     
1941     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1942         GENERIC_READ,
1943         FILE_SHARE_READ,
1944         NULL,
1945         OPEN_EXISTING,
1946         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
1947         NULL);
1948     if (hFile == INVALID_HANDLE_VALUE)
1949         return FALSE;
1950     
1951     /* allocate handle storage space */
1952     pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
1953     if (!pStream)
1954     {
1955         CloseHandle(hFile);
1956         SetLastError(ERROR_OUTOFMEMORY);
1957         return FALSE;
1958     }
1959
1960     pStream->hFile = hFile;
1961     strcpy(pStream->lpszUrl, lpszUrlName);
1962     return (HANDLE)pStream;
1963 }
1964
1965 /***********************************************************************
1966  *           UnlockUrlCacheEntryStream (WININET.@)
1967  *
1968  */
1969 BOOL WINAPI UnlockUrlCacheEntryStream(
1970     IN HANDLE hUrlCacheStream,
1971     IN DWORD dwReserved
1972 )
1973 {
1974     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1975
1976     if (dwReserved != 0)
1977     {
1978         ERR("dwReserved != 0\n");
1979         SetLastError(ERROR_INVALID_PARAMETER);
1980         return FALSE;
1981     }
1982
1983     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1984     {
1985         SetLastError(ERROR_INVALID_HANDLE);
1986         return FALSE;
1987     }
1988
1989     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
1990         return FALSE;
1991
1992     /* close file handle */
1993     CloseHandle(pStream->hFile);
1994
1995     /* free allocated space */
1996     HeapFree(GetProcessHeap(), 0, pStream);
1997
1998     return TRUE;
1999 }
2000
2001
2002 /***********************************************************************
2003  *           DeleteUrlCacheEntryA (WININET.@)
2004  *
2005  */
2006 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
2007 {
2008     URLCACHECONTAINER * pContainer;
2009     LPURLCACHE_HEADER pHeader;
2010     CACHEFILE_ENTRY * pEntry;
2011     DWORD dwStartBlock;
2012     DWORD dwBlock;
2013     BYTE * AllocationTable;
2014
2015     TRACE("(%s)\n", debugstr_a(lpszUrlName));
2016
2017     if (!URLCacheContainers_FindContainerA(lpszUrlName, &pContainer))
2018         return FALSE;
2019
2020     if (!URLCacheContainer_OpenIndex(pContainer))
2021         return FALSE;
2022
2023     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2024         return FALSE;
2025
2026     if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
2027     {
2028         URLCacheContainer_UnlockIndex(pContainer, pHeader);
2029         TRACE("entry %s not found!\n", lpszUrlName);
2030         SetLastError(ERROR_FILE_NOT_FOUND);
2031         return FALSE;
2032     }
2033
2034     AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
2035
2036     /* update allocation table */
2037     dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
2038     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
2039         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
2040
2041     URLCache_DeleteEntry(pEntry);
2042
2043     URLCache_DeleteEntryFromHash(pHeader, lpszUrlName);
2044
2045     URLCacheContainer_UnlockIndex(pContainer, pHeader);
2046
2047     return TRUE;
2048 }
2049
2050 /***********************************************************************
2051  *           FindFirstUrlCacheEntryA (WININET.@)
2052  *
2053  */
2054 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
2055  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2056 {
2057   FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2058   SetLastError(ERROR_FILE_NOT_FOUND);
2059   return 0;
2060 }
2061
2062 /***********************************************************************
2063  *           FindFirstUrlCacheEntryW (WININET.@)
2064  *
2065  */
2066 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
2067  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
2068 {
2069   FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
2070   return 0;
2071 }
2072
2073 /***********************************************************************
2074  *           CreateUrlCacheGroup (WININET.@)
2075  *
2076  */
2077 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID 
2078 lpReserved)
2079 {
2080   FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
2081   return FALSE;
2082 }
2083
2084 /***********************************************************************
2085  *           DeleteUrlCacheGroup (WININET.@)
2086  *
2087  */
2088 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
2089 {
2090     FIXME("STUB\n");
2091     return FALSE;
2092 }
2093
2094 /***********************************************************************
2095  *           SetUrlCacheEntryGroup (WININET.@)
2096  *
2097  */
2098 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
2099   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
2100   LPVOID lpReserved)
2101 {
2102     FIXME("STUB\n");
2103     SetLastError(ERROR_FILE_NOT_FOUND);
2104     return FALSE;
2105 }
2106
2107 /***********************************************************************
2108  *           GetUrlCacheConfigInfoW (WININET.@)
2109  */
2110 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2111 {
2112     FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2113     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2114     return FALSE;
2115 }
2116
2117 /***********************************************************************
2118  *           GetUrlCacheConfigInfoA (WININET.@)
2119  *
2120  * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
2121  */
2122 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
2123 {
2124     FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
2125     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2126     return FALSE;
2127 }