2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 Robert Shearman
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.
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.
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
25 #define COM_NO_WINDOWS_H
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
47 #define ENTRY_START_OFFSET 0x4000
50 #define CONTENT_DIRECTORY "\\Content.IE5\\"
51 #define HASHTABLE_SIZE 448
52 #define HASHTABLE_BLOCKSIZE 7
53 #define ALLOCATION_TABLE_OFFSET 0x250
54 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
55 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
57 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
58 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
59 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
60 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
61 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
63 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x) + 3) >> 2) << 2)
65 typedef struct _CACHEFILE_ENTRY
69 DWORD dwSignature
; /* e.g. "URL " */
70 /* CHAR szSignature[4];
72 DWORD dwBlocksUsed
; /* number of 128byte blocks used by this entry */
75 typedef struct _URL_CACHEFILE_ENTRY
77 CACHEFILE_ENTRY CacheFileEntry
;
78 FILETIME LastModifiedTime
;
79 FILETIME LastAccessTime
;
80 WORD wExpiredDate
; /* expire date in dos format */
81 WORD wExpiredTime
; /* expire time in dos format */
82 DWORD dwUnknown1
; /* usually zero */
83 DWORD dwSizeLow
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
84 DWORD dwSizeHigh
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
85 DWORD dwUnknown2
; /* usually zero */
86 DWORD dwExemptDelta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
87 DWORD dwUnknown3
; /* usually 0x60 */
88 DWORD dwOffsetUrl
; /* usually 0x68 */
89 BYTE CacheDir
; /* index of cache directory this url is stored in */
90 BYTE Unknown4
; /* usually zero */
91 WORD wUnknown5
; /* usually 0x1010 */
92 DWORD dwOffsetLocalName
; /* offset of start of local filename from start of entry */
93 DWORD CacheEntryType
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
94 DWORD dwOffsetHeaderInfo
; /* offset of start of header info from start of entry */
95 DWORD dwHeaderInfoSize
;
96 DWORD dwUnknown6
; /* usually zero */
97 WORD wLastSyncDate
; /* last sync date in dos format */
98 WORD wLastSyncTime
; /* last sync time in dos format */
99 DWORD dwHitRate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
100 DWORD dwUseCount
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
101 WORD wUnknownDate
; /* usually same as wLastSyncDate */
102 WORD wUnknownTime
; /* usually same as wLastSyncTime */
103 DWORD dwUnknown7
; /* usually zero */
104 DWORD dwUnknown8
; /* usually zero */
105 CHAR szSourceUrlName
[1]; /* start of url */
106 /* packing to dword align start of next field */
107 /* CHAR szLocalFileName[]; (local file name exluding path) */
108 /* packing to dword align start of next field */
109 /* CHAR szHeaderInfo[]; (header info) */
110 } URL_CACHEFILE_ENTRY
;
118 typedef struct _HASH_CACHEFILE_ENTRY
120 CACHEFILE_ENTRY CacheFileEntry
;
122 DWORD dwHashTableNumber
;
123 struct _HASH_ENTRY HashTable
[HASHTABLE_SIZE
];
124 } HASH_CACHEFILE_ENTRY
;
126 typedef struct _DIRECTORY_DATA
129 char filename
[DIR_LENGTH
];
132 typedef struct _URLCACHE_HEADER
134 char szSignature
[28];
136 DWORD dwOffsetFirstHashTable
;
137 DWORD dwIndexCapacityInBlocks
;
138 DWORD dwBlocksInUse
; /* is this right? */
140 DWORD dwCacheLimitLow
; /* disk space limit for cache */
141 DWORD dwCacheLimitHigh
; /* disk space limit for cache */
142 DWORD dwUnknown4
; /* current disk space usage for cache? */
143 DWORD dwUnknown5
; /* current disk space usage for cache? */
144 DWORD dwUnknown6
; /* possibly a flag? */
146 BYTE DirectoryCount
; /* number of directory_data's */
147 BYTE Unknown8
[3]; /* just padding? */
148 DIRECTORY_DATA directory_data
[1]; /* first directory entry */
149 } URLCACHE_HEADER
, *LPURLCACHE_HEADER
;
150 typedef const URLCACHE_HEADER
*LPCURLCACHE_HEADER
;
153 typedef struct _STREAM_HANDLE
159 /**** File Global Variables ****/
160 static HANDLE hCacheIndexMapping
= NULL
; /* handle to file mapping */
161 static LPSTR szCacheContentPath
= NULL
; /* path to content index */
162 static HANDLE hMutex
= NULL
;
164 /***********************************************************************
165 * URLCache_PathToObjectName (Internal)
167 * Converts a path to a name suitable for use as a Win32 object name.
168 * Replaces '\\' characters in-place with the specified character
169 * (usually '_' or '!')
175 static void URLCache_PathToObjectName(LPSTR lpszPath
, char replace
)
178 for (ch
= *lpszPath
; (ch
= *lpszPath
); lpszPath
++)
186 #define CHAR_BIT (8 * sizeof(CHAR))
189 /***********************************************************************
190 * URLCache_Allocation_BlockIsFree (Internal)
192 * Is the specified block number free?
199 static inline BYTE
URLCache_Allocation_BlockIsFree(BYTE
* AllocationTable
, DWORD dwBlockNumber
)
201 BYTE mask
= 1 << (dwBlockNumber
% CHAR_BIT
);
202 return (AllocationTable
[dwBlockNumber
/ CHAR_BIT
] & mask
) == 0;
205 /***********************************************************************
206 * URLCache_Allocation_BlockFree (Internal)
208 * Marks the specified block as free
214 static inline void URLCache_Allocation_BlockFree(BYTE
* AllocationTable
, DWORD dwBlockNumber
)
216 BYTE mask
= ~(1 << (dwBlockNumber
% CHAR_BIT
));
217 AllocationTable
[dwBlockNumber
/ CHAR_BIT
] &= mask
;
220 /***********************************************************************
221 * URLCache_Allocation_BlockAllocate (Internal)
223 * Marks the specified block as allocated
229 static inline void URLCache_Allocation_BlockAllocate(BYTE
* AllocationTable
, DWORD dwBlockNumber
)
231 BYTE mask
= 1 << (dwBlockNumber
% CHAR_BIT
);
232 AllocationTable
[dwBlockNumber
/ CHAR_BIT
] |= mask
;
235 /***********************************************************************
236 * URLCache_FindEntry (Internal)
238 * Finds an entry without using the hash tables
241 * TRUE if it found the specified entry
245 static BOOL
URLCache_FindEntry(LPCURLCACHE_HEADER pHeader
, LPCSTR szUrl
, CACHEFILE_ENTRY
** ppEntry
)
247 CACHEFILE_ENTRY
* pCurrentEntry
;
250 BYTE
* AllocationTable
= (LPBYTE
)pHeader
+ ALLOCATION_TABLE_OFFSET
;
252 for (pCurrentEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ ENTRY_START_OFFSET
);
253 (DWORD
)((LPBYTE
)pCurrentEntry
- (LPBYTE
)pHeader
) < pHeader
->dwFileSize
;
254 pCurrentEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pCurrentEntry
+ pCurrentEntry
->dwBlocksUsed
* BLOCKSIZE
))
256 dwBlockNumber
= (DWORD
)((LPBYTE
)pCurrentEntry
- (LPBYTE
)pHeader
- ENTRY_START_OFFSET
) / BLOCKSIZE
;
257 while (URLCache_Allocation_BlockIsFree(AllocationTable
, dwBlockNumber
))
259 if (dwBlockNumber
>= pHeader
->dwIndexCapacityInBlocks
)
262 pCurrentEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pCurrentEntry
+ BLOCKSIZE
);
263 dwBlockNumber
= (DWORD
)((LPBYTE
)pCurrentEntry
- (LPBYTE
)pHeader
- ENTRY_START_OFFSET
) / BLOCKSIZE
;
266 switch (pCurrentEntry
->dwSignature
)
268 case URL_SIGNATURE
: /* "URL " */
269 case LEAK_SIGNATURE
: /* "LEAK" */
271 URL_CACHEFILE_ENTRY
* pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pCurrentEntry
;
272 if (!strcmp(szUrl
, pUrlEntry
->szSourceUrlName
))
274 *ppEntry
= pCurrentEntry
;
275 /* FIXME: should we update the LastAccessTime here? */
280 case HASH_SIGNATURE
: /* HASH entries parsed in FindEntryInHash */
281 case 0xDEADBEEF: /* this is always at offset 0x4000 in URL cache for some reason */
284 FIXME("Unknown entry %.4s ignored\n", (LPCSTR
)&pCurrentEntry
->dwSignature
);
291 /***********************************************************************
292 * URLCache_OpenIndex (Internal)
294 * Opens the index file and saves mapping handle in hCacheIndexMapping
301 static BOOL
URLCache_OpenIndex()
304 CHAR szFullPath
[MAX_PATH
];
305 CHAR szFileMappingName
[MAX_PATH
+10];
306 CHAR szMutexName
[MAX_PATH
+1];
309 if (!szCacheContentPath
)
311 szCacheContentPath
= (LPSTR
)HeapAlloc(GetProcessHeap(), 0, MAX_PATH
* sizeof(CHAR
));
312 *szCacheContentPath
= '\0';
315 if (*szCacheContentPath
== '\0')
317 if (FAILED(SHGetSpecialFolderPathA(NULL
, szCacheContentPath
, CSIDL_INTERNET_CACHE
, TRUE
)))
319 strcat(szCacheContentPath
, CONTENT_DIRECTORY
);
322 strcpy(szFullPath
, szCacheContentPath
);
323 strcat(szFullPath
, "index.dat");
325 if (hCacheIndexMapping
)
328 hFile
= CreateFileA(szFullPath
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
329 if (hFile
== INVALID_HANDLE_VALUE
)
331 FIXME("need to create cache index file\n");
335 dwFileSize
= GetFileSize(hFile
, NULL
);
336 if (dwFileSize
== INVALID_FILE_SIZE
)
341 FIXME("need to create cache index file\n");
345 strcpy(szFileMappingName
, szFullPath
);
346 sprintf(szFileMappingName
+ strlen(szFileMappingName
), "\\%lu", dwFileSize
);
347 URLCache_PathToObjectName(szFileMappingName
, '_');
348 hCacheIndexMapping
= OpenFileMappingA(FILE_MAP_WRITE
, FALSE
, szFileMappingName
);
349 if (!hCacheIndexMapping
)
350 hCacheIndexMapping
= CreateFileMappingA(hFile
, NULL
, PAGE_READWRITE
, 0, 0, szFileMappingName
);
352 if (!hCacheIndexMapping
)
354 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
358 strcpy(szMutexName
, szFullPath
);
359 CharLowerA(szMutexName
);
360 URLCache_PathToObjectName(szMutexName
, '!');
361 strcat(szMutexName
, "!");
363 if ((hMutex
= CreateMutexA(NULL
, FALSE
, szMutexName
)) == NULL
)
365 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
366 CloseHandle(hCacheIndexMapping
);
373 /***********************************************************************
374 * URLCache_CloseIndex (Internal)
382 #if 0 /* not used at the moment */
383 static BOOL
URLCache_CloseIndex()
385 return CloseHandle(hCacheIndexMapping
);
389 /***********************************************************************
390 * URLCache_FindFirstFreeEntry (Internal)
392 * Finds and allocates the first block of free space big enough and
393 * sets ppEntry to point to it.
396 * TRUE if it had enough space
397 * FALSE if it couldn't find enough space
400 static BOOL
URLCache_FindFirstFreeEntry(URLCACHE_HEADER
* pHeader
, DWORD dwBlocksNeeded
, CACHEFILE_ENTRY
** ppEntry
)
402 LPBYTE AllocationTable
= (LPBYTE
)pHeader
+ ALLOCATION_TABLE_OFFSET
;
405 for (dwBlockNumber
= 0; dwBlockNumber
< pHeader
->dwIndexCapacityInBlocks
; dwBlockNumber
++)
407 for (dwFreeCounter
= 0;
408 dwFreeCounter
< dwBlocksNeeded
&&
409 dwFreeCounter
+ dwBlockNumber
< pHeader
->dwIndexCapacityInBlocks
&&
410 URLCache_Allocation_BlockIsFree(AllocationTable
, dwBlockNumber
+ dwFreeCounter
);
412 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber
, ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
414 if (dwFreeCounter
== dwBlocksNeeded
)
417 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber
, ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
418 for (index
= 0; index
< dwBlocksNeeded
; index
++)
419 URLCache_Allocation_BlockAllocate(AllocationTable
, dwBlockNumber
+ index
);
420 *ppEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
421 (*ppEntry
)->dwBlocksUsed
= dwBlocksNeeded
;
428 /***********************************************************************
429 * URLCache_DeleteEntry (Internal)
431 * Deletes the specified entry and frees the space allocated to it
434 * TRUE if it succeeded
438 static BOOL
URLCache_DeleteEntry(CACHEFILE_ENTRY
* pEntry
)
440 ZeroMemory(pEntry
, pEntry
->dwBlocksUsed
* BLOCKSIZE
);
444 /***********************************************************************
445 * URLCache_LockIndex (Internal)
448 static LPURLCACHE_HEADER
URLCache_LockIndex()
451 LPVOID pIndexData
= MapViewOfFile(hCacheIndexMapping
, FILE_MAP_WRITE
, 0, 0, 0);
452 URLCACHE_HEADER
* pHeader
= (URLCACHE_HEADER
*)pIndexData
;
456 TRACE("Signature: %s, file size: %ld bytes\n", pHeader
->szSignature
, pHeader
->dwFileSize
);
458 for (index
= 0; index
< pHeader
->DirectoryCount
; index
++)
460 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].filename
);
464 WaitForSingleObject(hMutex
, INFINITE
);
469 /***********************************************************************
470 * URLCache_UnlockIndex (Internal)
473 static BOOL
URLCache_UnlockIndex(LPURLCACHE_HEADER pHeader
)
476 ReleaseMutex(hMutex
);
477 return UnmapViewOfFile(pHeader
);
480 /***********************************************************************
481 * URLCache_LocalFileNameToPath (Internal)
483 * Copies the full path to the specified buffer given the local file
484 * name and the index of the directory it is in. Always sets value in
485 * lpBufferSize to the required buffer size.
488 * TRUE if the buffer was big enough
489 * FALSE if the buffer was too small
492 static BOOL
URLCache_LocalFileNameToPath(LPCURLCACHE_HEADER pHeader
, LPCSTR szLocalFileName
, BYTE Directory
, LPSTR szPath
, LPLONG lpBufferSize
)
495 if (Directory
>= pHeader
->DirectoryCount
)
501 nRequired
= (strlen(szCacheContentPath
) + DIR_LENGTH
+ strlen(szLocalFileName
) + 1) * sizeof(CHAR
);
502 if (nRequired
< *lpBufferSize
)
504 strcpy(szPath
, szCacheContentPath
);
505 strncat(szPath
, pHeader
->directory_data
[Directory
].filename
, DIR_LENGTH
);
506 strcat(szPath
, "\\");
507 strcat(szPath
, szLocalFileName
);
508 *lpBufferSize
= nRequired
;
511 *lpBufferSize
= nRequired
;
515 /***********************************************************************
516 * URLCache_CopyEntry (Internal)
518 * Copies an entry from the cache index file to the Win32 structure
521 * TRUE if the buffer was big enough
522 * FALSE if the buffer was too small
525 static BOOL
URLCache_CopyEntry(LPCURLCACHE_HEADER pHeader
, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
, LPDWORD lpdwBufferSize
, URL_CACHEFILE_ENTRY
* pUrlEntry
)
527 int lenUrl
= strlen(pUrlEntry
->szSourceUrlName
);
528 DWORD dwRequiredSize
= sizeof(*lpCacheEntryInfo
);
529 LONG nLocalFilePathSize
;
530 LPSTR lpszLocalFileName
;
532 if (*lpdwBufferSize
>= dwRequiredSize
)
534 lpCacheEntryInfo
->lpHeaderInfo
= NULL
;
535 lpCacheEntryInfo
->lpszFileExtension
= NULL
;
536 lpCacheEntryInfo
->lpszLocalFileName
= NULL
;
537 lpCacheEntryInfo
->lpszSourceUrlName
= NULL
;
538 lpCacheEntryInfo
->CacheEntryType
= pUrlEntry
->CacheEntryType
;
539 lpCacheEntryInfo
->u
.dwExemptDelta
= pUrlEntry
->dwExemptDelta
;
540 lpCacheEntryInfo
->dwHeaderInfoSize
= pUrlEntry
->dwHeaderInfoSize
;
541 lpCacheEntryInfo
->dwHitRate
= pUrlEntry
->dwHitRate
;
542 lpCacheEntryInfo
->dwSizeHigh
= pUrlEntry
->dwSizeHigh
;
543 lpCacheEntryInfo
->dwSizeLow
= pUrlEntry
->dwSizeLow
;
544 lpCacheEntryInfo
->dwStructSize
= sizeof(*lpCacheEntryInfo
);
545 lpCacheEntryInfo
->dwUseCount
= pUrlEntry
->dwUseCount
;
546 DosDateTimeToFileTime(pUrlEntry
->wExpiredDate
, pUrlEntry
->wExpiredTime
, &lpCacheEntryInfo
->ExpireTime
);
547 lpCacheEntryInfo
->LastAccessTime
.dwHighDateTime
= pUrlEntry
->LastAccessTime
.dwHighDateTime
;
548 lpCacheEntryInfo
->LastAccessTime
.dwLowDateTime
= pUrlEntry
->LastAccessTime
.dwLowDateTime
;
549 lpCacheEntryInfo
->LastModifiedTime
.dwHighDateTime
= pUrlEntry
->LastModifiedTime
.dwHighDateTime
;
550 lpCacheEntryInfo
->LastModifiedTime
.dwLowDateTime
= pUrlEntry
->LastModifiedTime
.dwLowDateTime
;
551 DosDateTimeToFileTime(pUrlEntry
->wLastSyncDate
, pUrlEntry
->wLastSyncTime
, &lpCacheEntryInfo
->LastSyncTime
);
554 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
555 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
556 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
557 dwRequiredSize
+= lenUrl
+ 1;
559 if (*lpdwBufferSize
>= dwRequiredSize
)
561 lpCacheEntryInfo
->lpszSourceUrlName
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
- lenUrl
- 1;
562 strcpy(lpCacheEntryInfo
->lpszSourceUrlName
, pUrlEntry
->szSourceUrlName
);
565 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
566 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
567 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
569 lpszLocalFileName
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
;
570 nLocalFilePathSize
= *lpdwBufferSize
- dwRequiredSize
;
571 if (URLCache_LocalFileNameToPath(pHeader
, (LPSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetLocalName
, pUrlEntry
->CacheDir
, lpszLocalFileName
, &nLocalFilePathSize
))
573 lpCacheEntryInfo
->lpszLocalFileName
= lpszLocalFileName
;
575 dwRequiredSize
+= nLocalFilePathSize
;
577 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
578 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
579 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
580 dwRequiredSize
+= pUrlEntry
->dwHeaderInfoSize
+ 1;
582 if (*lpdwBufferSize
>= dwRequiredSize
)
584 lpCacheEntryInfo
->lpHeaderInfo
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
- pUrlEntry
->dwHeaderInfoSize
- 1;
585 memcpy(lpCacheEntryInfo
->lpHeaderInfo
, (LPSTR
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
, pUrlEntry
->dwHeaderInfoSize
);
586 ((LPBYTE
)lpCacheEntryInfo
)[dwRequiredSize
- 1] = '\0';
588 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
589 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
590 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
592 if (dwRequiredSize
> *lpdwBufferSize
)
594 *lpdwBufferSize
= dwRequiredSize
;
595 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
598 *lpdwBufferSize
= dwRequiredSize
;
602 /***********************************************************************
603 * URLCache_HashKey (Internal)
605 * Returns the hash key for a given string
608 * hash key for the string
611 static DWORD
URLCache_HashKey(LPCSTR lpszKey
)
613 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
614 * but the algorithm and result are not the same!
616 static const unsigned char lookupTable
[256] =
618 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
619 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
620 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
621 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
622 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
623 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
624 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
625 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
626 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
627 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
628 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
629 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
630 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
631 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
632 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
633 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
634 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
635 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
636 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
637 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
638 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
639 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
640 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
641 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
642 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
643 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
644 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
645 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
646 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
647 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
648 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
649 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
653 int subscript
[sizeof(key
) / sizeof(key
[0])];
655 subscript
[0] = *lpszKey
;
656 subscript
[1] = (char)(*lpszKey
+ 1);
657 subscript
[2] = (char)(*lpszKey
+ 2);
658 subscript
[3] = (char)(*lpszKey
+ 3);
660 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
661 key
[i
] = lookupTable
[i
];
663 for (lpszKey
++; *lpszKey
&& ((lpszKey
[0] != '/') || (lpszKey
[1] != 0)); lpszKey
++)
665 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
666 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
669 return *(DWORD
*)key
;
672 static inline HASH_CACHEFILE_ENTRY
* URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader
, DWORD dwOffset
)
674 return (HASH_CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ dwOffset
);
677 /***********************************************************************
678 * URLCache_FindEntryInHash (Internal)
680 * Searches all the hash tables in the index for the given URL and
681 * returns the entry, if it was found, in ppEntry
684 * TRUE if the entry was found
685 * FALSE if the entry could not be found
688 static BOOL
URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader
, LPCSTR lpszUrl
, CACHEFILE_ENTRY
** ppEntry
)
690 /* structure of hash table:
691 * 448 entries divided into 64 blocks
692 * each block therefore contains a chain of 7 key/offset pairs
693 * how position in table is calculated:
694 * 1. the url is hashed in helper function
695 * 2. the key % 64 * 8 is the offset
696 * 3. the key in the hash table is the hash key aligned to 64
699 * there can be multiple hash tables in the file and the offset to
700 * the next one is stored in the header of the hash table
702 DWORD key
= URLCache_HashKey(lpszUrl
);
703 DWORD offset
= (key
% HASHTABLE_NUM_ENTRIES
) * sizeof(struct _HASH_ENTRY
);
704 HASH_CACHEFILE_ENTRY
* pHashEntry
;
705 DWORD dwHashTableNumber
= 0;
707 key
= (DWORD
)(key
/ HASHTABLE_NUM_ENTRIES
) * HASHTABLE_NUM_ENTRIES
;
709 for (pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHeader
->dwOffsetFirstHashTable
);
710 ((DWORD
)((LPBYTE
)pHashEntry
- (LPBYTE
)pHeader
) >= ENTRY_START_OFFSET
) && ((DWORD
)((LPBYTE
)pHashEntry
- (LPBYTE
)pHeader
) < pHeader
->dwFileSize
);
711 pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHashEntry
->dwAddressNext
))
714 if (pHashEntry
->dwHashTableNumber
!= dwHashTableNumber
++)
716 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry
->dwHashTableNumber
, dwHashTableNumber
);
719 /* make sure that it is in fact a hash entry */
720 if (pHashEntry
->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
)
722 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->CacheFileEntry
.dwSignature
);
726 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
728 struct _HASH_ENTRY
* pHashElement
= &pHashEntry
->HashTable
[offset
+ i
];
729 if (key
== (DWORD
)(pHashElement
->dwHashKey
/ HASHTABLE_NUM_ENTRIES
) * HASHTABLE_NUM_ENTRIES
)
731 *ppEntry
= (CACHEFILE_ENTRY
*)((LPBYTE
)pHeader
+ pHashElement
->dwOffsetEntry
);
739 /***********************************************************************
740 * URLCache_HashEntrySetUse (Internal)
742 * Searches all the hash tables in the index for the given URL and
743 * sets the use count (stored or'ed with key)
746 * TRUE if the entry was found
747 * FALSE if the entry could not be found
750 static BOOL
URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader
, LPCSTR lpszUrl
, DWORD dwUseCount
)
752 /* see URLCache_FindEntryInHash for structure of hash tables */
754 DWORD key
= URLCache_HashKey(lpszUrl
);
755 DWORD offset
= (key
% HASHTABLE_NUM_ENTRIES
) * sizeof(struct _HASH_ENTRY
);
756 HASH_CACHEFILE_ENTRY
* pHashEntry
;
757 DWORD dwHashTableNumber
= 0;
759 if (dwUseCount
>= HASHTABLE_NUM_ENTRIES
)
761 ERR("don't know what to do when use count exceeds %d, guessing\n", HASHTABLE_NUM_ENTRIES
);
762 dwUseCount
= HASHTABLE_NUM_ENTRIES
- 1;
765 key
= (DWORD
)(key
/ HASHTABLE_NUM_ENTRIES
) * HASHTABLE_BLOCKSIZE
;
767 for (pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHeader
->dwOffsetFirstHashTable
);
768 ((DWORD
)((LPBYTE
)pHashEntry
- (LPBYTE
)pHeader
) >= ENTRY_START_OFFSET
) && ((DWORD
)((LPBYTE
)pHashEntry
- (LPBYTE
)pHeader
) < pHeader
->dwFileSize
);
769 pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHashEntry
->dwAddressNext
))
772 if (pHashEntry
->dwHashTableNumber
!= dwHashTableNumber
++)
774 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry
->dwHashTableNumber
, dwHashTableNumber
);
777 /* make sure that it is in fact a hash entry */
778 if (pHashEntry
->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
)
780 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->CacheFileEntry
.dwSignature
);
784 for (i
= 0; i
< 7; i
++)
786 struct _HASH_ENTRY
* pHashElement
= &pHashEntry
->HashTable
[offset
+ i
];
787 if (key
== (DWORD
)(pHashElement
->dwHashKey
/ HASHTABLE_NUM_ENTRIES
) * HASHTABLE_NUM_ENTRIES
)
789 pHashElement
->dwOffsetEntry
= dwUseCount
| (DWORD
)(pHashElement
->dwHashKey
/ HASHTABLE_NUM_ENTRIES
) * HASHTABLE_NUM_ENTRIES
;
797 /***********************************************************************
798 * URLCache_AddEntryToHash (Internal)
800 * Searches all the hash tables for a free slot based on the offset
801 * generated from the hash key. If a free slot is found, the offset and
802 * key are entered into the hash table.
805 * TRUE if the entry was added
806 * FALSE if the entry could not be added
809 static BOOL
URLCache_AddEntryToHash(LPCURLCACHE_HEADER pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
)
811 /* see URLCache_FindEntryInHash for structure of hash tables */
813 DWORD key
= URLCache_HashKey(lpszUrl
);
814 DWORD offset
= (key
% HASHTABLE_NUM_ENTRIES
) * sizeof(struct _HASH_ENTRY
);
815 HASH_CACHEFILE_ENTRY
* pHashEntry
;
816 DWORD dwHashTableNumber
= 0;
818 key
= (DWORD
)(key
/ HASHTABLE_NUM_ENTRIES
) * HASHTABLE_NUM_ENTRIES
;
820 for (pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHeader
->dwOffsetFirstHashTable
);
821 ((DWORD
)((LPBYTE
)pHashEntry
- (LPBYTE
)pHeader
) >= ENTRY_START_OFFSET
) && ((DWORD
)((LPBYTE
)pHashEntry
- (LPBYTE
)pHeader
) < pHeader
->dwFileSize
);
822 pHashEntry
= URLCache_HashEntryFromOffset(pHeader
, pHashEntry
->dwAddressNext
))
825 if (pHashEntry
->dwHashTableNumber
!= dwHashTableNumber
++)
827 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry
->dwHashTableNumber
, dwHashTableNumber
);
830 /* make sure that it is in fact a hash entry */
831 if (pHashEntry
->CacheFileEntry
.dwSignature
!= HASH_SIGNATURE
)
833 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->CacheFileEntry
.dwSignature
);
837 for (i
= 0; i
< 7; i
++)
839 struct _HASH_ENTRY
* pHashElement
= &pHashEntry
->HashTable
[offset
+ i
];
840 if (pHashElement
->dwHashKey
== 3 /* FIXME: just 3? */) /* if the slot is free */
842 pHashElement
->dwHashKey
= key
;
843 pHashElement
->dwOffsetEntry
= dwOffsetEntry
;
848 FIXME("need to create another hash table\n");
852 /***********************************************************************
853 * GetUrlCacheEntryInfoExA (WININET.@)
856 BOOL WINAPI
GetUrlCacheEntryInfoExA(
858 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
859 LPDWORD lpdwCacheEntryInfoBufSize
,
861 LPDWORD lpdwReserved
,
865 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
868 lpdwCacheEntryInfoBufSize
,
874 if ((lpszReserved
!= NULL
) ||
875 (lpdwReserved
!= NULL
) ||
876 (lpReserved
!= NULL
))
878 ERR("Reserved value was not 0\n");
879 SetLastError(ERROR_INVALID_PARAMETER
);
883 FIXME("Undocumented flag(s): %lx\n", dwFlags
);
884 return GetUrlCacheEntryInfoA(lpszUrl
, lpCacheEntryInfo
, lpdwCacheEntryInfoBufSize
);
887 /***********************************************************************
888 * GetUrlCacheEntryInfoA (WININET.@)
891 BOOL WINAPI
GetUrlCacheEntryInfoA(
892 IN LPCSTR lpszUrlName
,
893 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
894 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
897 LPURLCACHE_HEADER pHeader
;
898 CACHEFILE_ENTRY
* pEntry
;
899 URL_CACHEFILE_ENTRY
* pUrlEntry
;
901 if (!URLCache_OpenIndex())
904 if (!(pHeader
= URLCache_LockIndex()))
907 if (!URLCache_FindEntryInHash(pHeader
, lpszUrlName
, &pEntry
))
909 if (!URLCache_FindEntry(pHeader
, lpszUrlName
, &pEntry
))
911 URLCache_UnlockIndex(pHeader
);
912 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
913 SetLastError(ERROR_FILE_NOT_FOUND
);
918 /* FIXME: check signature */
920 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
921 TRACE("Found URL: %s\n", pUrlEntry
->szSourceUrlName
);
922 TRACE("Header info: %s\n", (LPBYTE
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
);
924 if (!URLCache_CopyEntry(pHeader
, lpCacheEntryInfo
, lpdwCacheEntryInfoBufferSize
, pUrlEntry
))
926 URLCache_UnlockIndex(pHeader
);
929 TRACE("Local File Name: %s\n", lpCacheEntryInfo
->lpszLocalFileName
);
931 URLCache_UnlockIndex(pHeader
);
936 /***********************************************************************
937 * RetrieveUrlCacheEntryFileA (WININET.@)
940 BOOL WINAPI
RetrieveUrlCacheEntryFileA(
941 IN LPCSTR lpszUrlName
,
942 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
943 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
947 LPURLCACHE_HEADER pHeader
;
948 CACHEFILE_ENTRY
* pEntry
;
949 URL_CACHEFILE_ENTRY
* pUrlEntry
;
951 if (!URLCache_OpenIndex())
954 if (!(pHeader
= URLCache_LockIndex()))
957 if (!URLCache_FindEntryInHash(pHeader
, lpszUrlName
, &pEntry
))
959 if (!URLCache_FindEntry(pHeader
, lpszUrlName
, &pEntry
))
961 URLCache_UnlockIndex(pHeader
);
962 TRACE("entry %s not found!\n", lpszUrlName
);
963 SetLastError(ERROR_FILE_NOT_FOUND
);
968 /* FIXME: check signature */
970 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
971 TRACE("Found URL: %s\n", pUrlEntry
->szSourceUrlName
);
972 TRACE("Header info: %s\n", (LPBYTE
)pUrlEntry
+ pUrlEntry
->dwOffsetHeaderInfo
);
974 pUrlEntry
->dwHitRate
++;
975 pUrlEntry
->dwUseCount
++;
976 URLCache_HashEntrySetUse(pHeader
, lpszUrlName
, pUrlEntry
->dwUseCount
);
978 if (!URLCache_CopyEntry(pHeader
, lpCacheEntryInfo
, lpdwCacheEntryInfoBufferSize
, pUrlEntry
))
980 URLCache_UnlockIndex(pHeader
);
983 TRACE("Local File Name: %s\n", lpCacheEntryInfo
->lpszLocalFileName
);
985 URLCache_UnlockIndex(pHeader
);
990 /***********************************************************************
991 * UnlockUrlCacheEntryFileA (WININET.@)
994 BOOL WINAPI
UnlockUrlCacheEntryFileA(
995 IN LPCSTR lpszUrlName
,
999 LPURLCACHE_HEADER pHeader
;
1000 CACHEFILE_ENTRY
* pEntry
;
1001 URL_CACHEFILE_ENTRY
* pUrlEntry
;
1005 ERR("dwReserved != 0\n");
1006 SetLastError(ERROR_INVALID_PARAMETER
);
1010 if (!URLCache_OpenIndex())
1013 if (!(pHeader
= URLCache_LockIndex()))
1016 if (!URLCache_FindEntryInHash(pHeader
, lpszUrlName
, &pEntry
))
1018 if (!URLCache_FindEntry(pHeader
, lpszUrlName
, &pEntry
))
1020 URLCache_UnlockIndex(pHeader
);
1021 TRACE("entry %s not found!\n", lpszUrlName
);
1022 SetLastError(ERROR_FILE_NOT_FOUND
);
1027 /* FIXME: check signature */
1029 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
1031 if (pUrlEntry
->dwUseCount
== 0)
1033 URLCache_UnlockIndex(pHeader
);
1036 pUrlEntry
->dwUseCount
--;
1037 URLCache_HashEntrySetUse(pHeader
, lpszUrlName
, pUrlEntry
->dwUseCount
);
1039 URLCache_UnlockIndex(pHeader
);
1044 /***********************************************************************
1045 * CreateUrlCacheEntryA (WININET.@)
1048 BOOL WINAPI
CreateUrlCacheEntryA(
1049 IN LPCSTR lpszUrlName
,
1050 IN DWORD dwExpectedFileSize
,
1051 IN LPCSTR lpszFileExtension
,
1052 OUT LPSTR lpszFileName
,
1056 LPURLCACHE_HEADER pHeader
;
1057 CHAR szFile
[MAX_PATH
];
1058 CHAR szExtension
[MAX_PATH
];
1061 LPCSTR lpszFileNameExtension
;
1062 LPSTR lpszFileNameNoPath
;
1064 int countnoextension
;
1066 LONG lBufferSize
= MAX_PATH
* sizeof(CHAR
);
1067 BOOL bFound
= FALSE
;
1072 ERR("dwReserved != 0\n");
1073 SetLastError(ERROR_INVALID_PARAMETER
);
1077 for (lpszUrlEnd
= lpszUrlName
; *lpszUrlEnd
; lpszUrlEnd
++)
1080 if (((lpszUrlEnd
- lpszUrlName
) > 1) && (*(lpszUrlEnd
- 1) == '/'))
1083 for (lpszUrlPart
= lpszUrlEnd
;
1084 (lpszUrlPart
>= lpszUrlName
);
1087 if ((*lpszUrlPart
== '/') && ((lpszUrlEnd
- lpszUrlPart
) > 1))
1094 if (!strcmp(lpszUrlPart
, "www"))
1096 lpszUrlPart
+= strlen("www");
1099 count
= lpszUrlEnd
- lpszUrlPart
;
1101 if (bFound
&& (count
< MAX_PATH
))
1103 memcpy(szFile
, lpszUrlPart
, count
* sizeof(CHAR
));
1104 szFile
[count
] = '\0';
1105 /* FIXME: get rid of illegal characters like \, / and : */
1109 FIXME("need to generate a random filename");
1112 TRACE("File name: %s\n", szFile
);
1114 if (!URLCache_OpenIndex())
1117 if (!(pHeader
= URLCache_LockIndex()))
1120 CacheDir
= (BYTE
)(rand() % pHeader
->DirectoryCount
);
1122 URLCache_LocalFileNameToPath(pHeader
, szFile
, CacheDir
, lpszFileName
, &lBufferSize
);
1124 URLCache_UnlockIndex(pHeader
);
1126 lpszFileNameNoPath
= lpszFileName
+ strlen(szCacheContentPath
) + DIR_LENGTH
+ 1;
1128 countnoextension
= strlen(lpszFileNameNoPath
);
1129 lpszFileNameExtension
= PathFindExtensionA(lpszFileNameNoPath
);
1130 if (lpszFileNameExtension
)
1131 countnoextension
-= strlen(lpszFileNameExtension
);
1132 *szExtension
= '\0';
1134 if (lpszFileExtension
)
1136 szExtension
[0] = '.';
1137 strcpy(szExtension
+1, lpszFileExtension
);
1140 for (i
= 0; i
< 255; i
++)
1143 strncpy(lpszFileNameNoPath
, szFile
, countnoextension
);
1144 sprintf(lpszFileNameNoPath
+ countnoextension
, "[%u]%s", i
, szExtension
);
1145 TRACE("Trying: %s\n", lpszFileName
);
1146 hFile
= CreateFileA(lpszFileName
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
1147 if (hFile
!= INVALID_HANDLE_VALUE
)
1157 /***********************************************************************
1158 * CommitUrlCacheEntryA (WININET.@)
1161 BOOL WINAPI
CommitUrlCacheEntryA(
1162 IN LPCSTR lpszUrlName
,
1163 IN LPCSTR lpszLocalFileName
,
1164 IN FILETIME ExpireTime
,
1165 IN FILETIME LastModifiedTime
,
1166 IN DWORD CacheEntryType
,
1167 IN LPBYTE lpHeaderInfo
,
1168 IN DWORD dwHeaderSize
,
1169 IN LPCSTR lpszFileExtension
,
1170 IN LPCSTR dwReserved
1173 LPURLCACHE_HEADER pHeader
;
1174 CACHEFILE_ENTRY
* pEntry
;
1175 URL_CACHEFILE_ENTRY
* pUrlEntry
;
1176 DWORD dwBytesNeeded
= sizeof(*pUrlEntry
) - sizeof(pUrlEntry
->szSourceUrlName
);
1178 BOOL bFound
= FALSE
;
1179 DWORD dwOffsetLocalFileName
;
1180 DWORD dwOffsetHeader
;
1181 DWORD dwFileSizeLow
;
1182 DWORD dwFileSizeHigh
;
1185 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1186 debugstr_a(lpszUrlName
),
1187 debugstr_a(lpszLocalFileName
),
1196 ERR("dwReserved != 0\n");
1197 SetLastError(ERROR_INVALID_PARAMETER
);
1200 if (lpHeaderInfo
== NULL
)
1202 FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1205 hFile
= CreateFileA(lpszLocalFileName
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
1206 if (hFile
== INVALID_HANDLE_VALUE
)
1208 ERR("couldn't open file (error is %ld)\n", GetLastError());
1213 dwFileSizeLow
= GetFileSize(hFile
, &dwFileSizeHigh
);
1214 if ((dwFileSizeLow
== -1) && (GetLastError() != NO_ERROR
))
1216 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1223 if (!URLCache_OpenIndex())
1226 if (!(pHeader
= URLCache_LockIndex()))
1229 if (URLCache_FindEntryInHash(pHeader
, lpszUrlName
, &pEntry
) || URLCache_FindEntry(pHeader
, lpszUrlName
, &pEntry
))
1231 URLCache_UnlockIndex(pHeader
);
1232 FIXME("entry already in cache - don't know what to do!\n");
1233 SetLastError(ERROR_FILE_NOT_FOUND
);
1237 if (memcmp(lpszLocalFileName
, szCacheContentPath
, strlen(szCacheContentPath
)))
1239 URLCache_UnlockIndex(pHeader
);
1240 ERR("path must begin with cache content path\n");
1241 SetLastError(ERROR_INVALID_PARAMETER
);
1245 lpszLocalFileName
+= strlen(szCacheContentPath
);
1247 for (cDirectory
= 0; cDirectory
< pHeader
->DirectoryCount
; cDirectory
++)
1249 if (!strncmp(pHeader
->directory_data
[cDirectory
].filename
, lpszLocalFileName
, DIR_LENGTH
))
1258 URLCache_UnlockIndex(pHeader
);
1259 ERR("cache directory not found in path %s\n", lpszLocalFileName
);
1260 SetLastError(ERROR_INVALID_PARAMETER
);
1264 lpszLocalFileName
+= (DIR_LENGTH
+ 1); /* "1234WXYZ\" */
1266 dwOffsetLocalFileName
= DWORD_ALIGN(dwBytesNeeded
+ strlen(lpszUrlName
) + 1); /* + 1 for NULL terminating char */
1267 dwOffsetHeader
= DWORD_ALIGN(dwOffsetLocalFileName
+ strlen(lpszLocalFileName
) + 1);
1268 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ dwHeaderSize
);
1270 if (dwBytesNeeded
% BLOCKSIZE
)
1272 dwBytesNeeded
-= dwBytesNeeded
% BLOCKSIZE
;
1273 dwBytesNeeded
+= BLOCKSIZE
;
1276 if (!URLCache_FindFirstFreeEntry(pHeader
, dwBytesNeeded
/ BLOCKSIZE
, &pEntry
))
1278 /* we should grow the index file here */
1279 URLCache_UnlockIndex(pHeader
);
1280 FIXME("no free entries\n");
1284 /* FindFirstFreeEntry fills in blocks used */
1285 pUrlEntry
= (URL_CACHEFILE_ENTRY
*)pEntry
;
1286 pUrlEntry
->CacheFileEntry
.dwSignature
= URL_SIGNATURE
;
1287 pUrlEntry
->CacheDir
= cDirectory
;
1288 pUrlEntry
->CacheEntryType
= CacheEntryType
;
1289 pUrlEntry
->dwHeaderInfoSize
= dwHeaderSize
;
1290 pUrlEntry
->dwExemptDelta
= 0;
1291 pUrlEntry
->dwHitRate
= 0;
1292 pUrlEntry
->dwOffsetHeaderInfo
= dwOffsetHeader
;
1293 pUrlEntry
->dwOffsetLocalName
= dwOffsetLocalFileName
;
1294 pUrlEntry
->dwOffsetUrl
= sizeof(*pUrlEntry
) - sizeof(pUrlEntry
->szSourceUrlName
);
1295 pUrlEntry
->dwSizeHigh
= 0;
1296 pUrlEntry
->dwSizeLow
= dwFileSizeLow
;
1297 pUrlEntry
->dwSizeHigh
= dwFileSizeHigh
;
1298 pUrlEntry
->dwUseCount
= 0;
1299 GetSystemTimeAsFileTime(&pUrlEntry
->LastAccessTime
);
1300 pUrlEntry
->LastModifiedTime
= LastModifiedTime
;
1301 FileTimeToDosDateTime(&pUrlEntry
->LastAccessTime
, &pUrlEntry
->wLastSyncDate
, &pUrlEntry
->wLastSyncTime
);
1302 FileTimeToDosDateTime(&ExpireTime
, &pUrlEntry
->wExpiredDate
, &pUrlEntry
->wExpiredTime
);
1303 pUrlEntry
->wUnknownDate
= pUrlEntry
->wLastSyncDate
;
1304 pUrlEntry
->wUnknownTime
= pUrlEntry
->wLastSyncTime
;
1307 pUrlEntry
->dwUnknown1
= 0;
1308 pUrlEntry
->dwUnknown2
= 0;
1309 pUrlEntry
->dwUnknown3
= 0x60;
1310 pUrlEntry
->Unknown4
= 0;
1311 pUrlEntry
->wUnknown5
= 0x1010;
1312 pUrlEntry
->dwUnknown6
= 0;
1313 pUrlEntry
->dwUnknown7
= 0;
1314 pUrlEntry
->dwUnknown8
= 0;
1316 strcpy(pUrlEntry
->szSourceUrlName
, lpszUrlName
);
1317 strcpy((LPSTR
)((LPBYTE
)pUrlEntry
+ dwOffsetLocalFileName
), lpszLocalFileName
);
1318 memcpy((LPSTR
)((LPBYTE
)pUrlEntry
+ dwOffsetHeader
), lpHeaderInfo
, dwHeaderSize
);
1320 if (!URLCache_AddEntryToHash(pHeader
, lpszUrlName
, (DWORD
)((LPBYTE
)pUrlEntry
- (LPBYTE
)pHeader
)))
1322 URLCache_UnlockIndex(pHeader
);
1326 URLCache_UnlockIndex(pHeader
);
1331 BOOL WINAPI
ReadUrlCacheEntryStream(
1332 IN HANDLE hUrlCacheStream
,
1333 IN DWORD dwLocation
,
1334 IN OUT LPVOID lpBuffer
,
1335 IN OUT LPDWORD lpdwLen
,
1339 /* Get handle to file from 'stream' */
1340 STREAM_HANDLE
* pStream
= (STREAM_HANDLE
*)hUrlCacheStream
;
1342 if (dwReserved
!= 0)
1344 ERR("dwReserved != 0\n");
1345 SetLastError(ERROR_INVALID_PARAMETER
);
1349 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->lpszUrl
, INTERNET_MAX_URL_LENGTH
))
1351 SetLastError(ERROR_INVALID_HANDLE
);
1355 if (SetFilePointer(pStream
->hFile
, dwLocation
, NULL
, FILE_CURRENT
) == -1)
1357 return ReadFile(pStream
->hFile
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
1360 /***********************************************************************
1361 * RetrieveUrlCacheEntryStreamA (WININET.@)
1364 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(
1365 IN LPCSTR lpszUrlName
,
1366 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1367 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
1368 IN BOOL fRandomRead
,
1372 /* NOTE: this is not the same as the way that the native
1373 * version allocates 'stream' handles. I did it this way
1374 * as it is much easier and no applications should depend
1375 * on this behaviour. (Native version appears to allocate
1376 * indices into a table)
1378 STREAM_HANDLE
* pStream
;
1381 if (!RetrieveUrlCacheEntryFileA(lpszUrlName
,
1383 lpdwCacheEntryInfoBufferSize
,
1389 hFile
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
,
1394 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
1396 if (hFile
== INVALID_HANDLE_VALUE
)
1399 /* allocate handle storage space */
1400 pStream
= (STREAM_HANDLE
*)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE
) + strlen(lpszUrlName
) * sizeof(CHAR
));
1404 SetLastError(ERROR_OUTOFMEMORY
);
1408 pStream
->hFile
= hFile
;
1409 strcpy(pStream
->lpszUrl
, lpszUrlName
);
1410 return (HANDLE
)pStream
;
1413 /***********************************************************************
1414 * UnlockUrlCacheEntryStream (WININET.@)
1417 BOOL WINAPI
UnlockUrlCacheEntryStream(
1418 IN HANDLE hUrlCacheStream
,
1422 STREAM_HANDLE
* pStream
= (STREAM_HANDLE
*)hUrlCacheStream
;
1424 if (dwReserved
!= 0)
1426 ERR("dwReserved != 0\n");
1427 SetLastError(ERROR_INVALID_PARAMETER
);
1431 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->lpszUrl
, INTERNET_MAX_URL_LENGTH
))
1433 SetLastError(ERROR_INVALID_HANDLE
);
1437 if (!UnlockUrlCacheEntryFileA(pStream
->lpszUrl
, 0))
1440 /* close file handle */
1441 CloseHandle(pStream
->hFile
);
1443 /* free allocated space */
1444 HeapFree(GetProcessHeap(), 0, pStream
);
1450 /***********************************************************************
1451 * DeleteUrlCacheEntryA (WININET.@)
1454 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
1456 LPURLCACHE_HEADER pHeader
;
1457 CACHEFILE_ENTRY
* pEntry
;
1460 BYTE
* AllocationTable
;
1462 if (!URLCache_OpenIndex())
1465 if (!(pHeader
= URLCache_LockIndex()))
1468 if (!URLCache_FindEntryInHash(pHeader
, lpszUrlName
, &pEntry
))
1470 if (!URLCache_FindEntry(pHeader
, lpszUrlName
, &pEntry
))
1472 URLCache_UnlockIndex(pHeader
);
1473 TRACE("entry %s not found!\n", lpszUrlName
);
1474 SetLastError(ERROR_FILE_NOT_FOUND
);
1479 AllocationTable
= (LPBYTE
)pHeader
+ ALLOCATION_TABLE_OFFSET
;
1481 /* update allocation table */
1482 dwStartBlock
= ((DWORD
)pEntry
- (DWORD
)pHeader
) / BLOCKSIZE
;
1483 for (dwBlock
= dwStartBlock
; dwBlock
< dwStartBlock
+ pEntry
->dwBlocksUsed
; dwBlock
++)
1484 URLCache_Allocation_BlockFree(AllocationTable
, dwBlock
);
1486 URLCache_DeleteEntry(pEntry
);
1488 /* FIXME: update hash table */
1490 URLCache_UnlockIndex(pHeader
);
1495 /***********************************************************************
1496 * CreateUrlCacheGroup (WININET.@)
1499 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID
1502 FIXME("(%lx, %p): stub\n", dwFlags
, lpReserved
);
1506 /***********************************************************************
1507 * FindFirstUrlCacheEntryA (WININET.@)
1510 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
1511 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
1513 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
1517 /***********************************************************************
1518 * FindFirstUrlCacheEntryW (WININET.@)
1521 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
1522 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
1524 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
1528 /***********************************************************************
1529 * DeleteUrlCacheGroup (WININET.@)
1532 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
1538 /***********************************************************************
1539 * SetUrlCacheEntryGroup (WININET.@)
1542 BOOL WINAPI
SetUrlCacheEntryGroup(LPCSTR lpszUrlName
, DWORD dwFlags
,
1543 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
1547 SetLastError(ERROR_FILE_NOT_FOUND
);
1551 /***********************************************************************
1552 * GetUrlCacheEntryInfoW (WININET.@)
1555 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1556 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntry
,
1557 LPDWORD lpCacheEntrySize
)
1559 FIXME("(%s) stub\n",debugstr_w(lpszUrl
));
1560 SetLastError(ERROR_FILE_NOT_FOUND
);
1564 /***********************************************************************
1565 * GetUrlCacheEntryInfoExW (WININET.@)
1568 BOOL WINAPI
GetUrlCacheEntryInfoExW(
1570 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1571 LPDWORD lpdwCacheEntryInfoBufSize
,
1572 LPWSTR lpszReserved
,
1573 LPDWORD lpdwReserved
,
1577 FIXME(" url=%s, flags=%ld\n",debugstr_w(lpszUrl
),dwFlags
);
1578 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND
);
1582 /***********************************************************************
1583 * GetUrlCacheConfigInfoA (WININET.@)
1585 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
1587 BOOL WINAPI
GetUrlCacheConfigInfoA(LPDWORD CacheInfo
, LPDWORD size
, DWORD bitmask
)
1589 FIXME("(%p, %p, %lx)\n", CacheInfo
, size
, bitmask
);
1590 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
1594 /***********************************************************************
1595 * GetUrlCacheConfigInfoW (WININET.@)
1597 BOOL WINAPI
GetUrlCacheConfigInfoW(LPDWORD CacheInfo
, LPDWORD size
, DWORD bitmask
)
1599 FIXME("(%p, %p, %lx)\n", CacheInfo
, size
, bitmask
);
1600 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
1605 /***********************************************************************
1606 * SetUrlCacheEntryInfoA (WININET.@)
1608 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
, DWORD dwFieldControl
)
1614 /***********************************************************************
1615 * SetUrlCacheEntryInfoW (WININET.@)
1617 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrlName
, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
, DWORD dwFieldControl
)