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 BOOL WINAPI RetrieveUrlCacheEntryFileA(
937 IN LPCSTR lpszUrlName,
938 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
939 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
943 LPURLCACHE_HEADER pHeader;
944 CACHEFILE_ENTRY * pEntry;
945 URL_CACHEFILE_ENTRY * pUrlEntry;
947 if (!URLCache_OpenIndex())
950 if (!(pHeader = URLCache_LockIndex()))
953 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
955 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
957 URLCache_UnlockIndex(pHeader);
958 TRACE("entry %s not found!\n", lpszUrlName);
959 SetLastError(ERROR_FILE_NOT_FOUND);
964 /* FIXME: check signature */
966 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
967 TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
968 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
970 pUrlEntry->dwHitRate++;
971 pUrlEntry->dwUseCount++;
972 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
974 if (!URLCache_CopyEntry(pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry))
976 URLCache_UnlockIndex(pHeader);
979 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
981 URLCache_UnlockIndex(pHeader);
986 BOOL WINAPI UnlockUrlCacheEntryFileA(
987 IN LPCSTR lpszUrlName,
991 LPURLCACHE_HEADER pHeader;
992 CACHEFILE_ENTRY * pEntry;
993 URL_CACHEFILE_ENTRY * pUrlEntry;
997 ERR("dwReserved != 0\n");
998 SetLastError(ERROR_INVALID_PARAMETER);
1002 if (!URLCache_OpenIndex())
1005 if (!(pHeader = URLCache_LockIndex()))
1008 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1010 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
1012 URLCache_UnlockIndex(pHeader);
1013 TRACE("entry %s not found!\n", lpszUrlName);
1014 SetLastError(ERROR_FILE_NOT_FOUND);
1019 /* FIXME: check signature */
1021 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1023 if (pUrlEntry->dwUseCount == 0)
1025 URLCache_UnlockIndex(pHeader);
1028 pUrlEntry->dwUseCount--;
1029 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1031 URLCache_UnlockIndex(pHeader);
1036 /***********************************************************************
1037 * CreateUrlCacheEntryA (WININET.@)
1040 BOOL WINAPI CreateUrlCacheEntryA(
1041 IN LPCSTR lpszUrlName,
1042 IN DWORD dwExpectedFileSize,
1043 IN LPCSTR lpszFileExtension,
1044 OUT LPSTR lpszFileName,
1048 LPURLCACHE_HEADER pHeader;
1049 CHAR szFile[MAX_PATH];
1050 CHAR szExtension[MAX_PATH];
1053 LPCSTR lpszFileNameExtension;
1054 LPSTR lpszFileNameNoPath;
1056 int countnoextension;
1058 LONG lBufferSize = MAX_PATH * sizeof(CHAR);
1059 BOOL bFound = FALSE;
1064 ERR("dwReserved != 0\n");
1065 SetLastError(ERROR_INVALID_PARAMETER);
1069 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1072 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1075 for (lpszUrlPart = lpszUrlEnd;
1076 (lpszUrlPart >= lpszUrlName);
1079 if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1086 if (!strcmp(lpszUrlPart, "www"))
1088 lpszUrlPart += strlen("www");
1091 count = lpszUrlEnd - lpszUrlPart;
1093 if (bFound && (count < MAX_PATH))
1095 memcpy(szFile, lpszUrlPart, count * sizeof(CHAR));
1096 szFile[count] = '\0';
1097 /* FIXME: get rid of illegal characters like \, / and : */
1101 FIXME("need to generate a random filename");
1104 TRACE("File name: %s\n", szFile);
1106 if (!URLCache_OpenIndex())
1109 if (!(pHeader = URLCache_LockIndex()))
1112 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1114 URLCache_LocalFileNameToPath(pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1116 URLCache_UnlockIndex(pHeader);
1118 lpszFileNameNoPath = lpszFileName + strlen(szCacheContentPath) + DIR_LENGTH + 1;
1120 countnoextension = strlen(lpszFileNameNoPath);
1121 lpszFileNameExtension = PathFindExtensionA(lpszFileNameNoPath);
1122 if (lpszFileNameExtension)
1123 countnoextension -= strlen(lpszFileNameExtension);
1124 *szExtension = '\0';
1126 if (lpszFileExtension)
1128 szExtension[0] = '.';
1129 strcpy(szExtension+1, lpszFileExtension);
1132 for (i = 0; i < 255; i++)
1135 strncpy(lpszFileNameNoPath, szFile, countnoextension);
1136 sprintf(lpszFileNameNoPath + countnoextension, "[%u]%s", i, szExtension);
1137 TRACE("Trying: %s\n", lpszFileName);
1138 hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1139 if (hFile != INVALID_HANDLE_VALUE)
1149 /***********************************************************************
1150 * CommitUrlCacheEntryA (WININET.@)
1153 BOOL WINAPI CommitUrlCacheEntryA(
1154 IN LPCSTR lpszUrlName,
1155 IN LPCSTR lpszLocalFileName,
1156 IN FILETIME ExpireTime,
1157 IN FILETIME LastModifiedTime,
1158 IN DWORD CacheEntryType,
1159 IN LPBYTE lpHeaderInfo,
1160 IN DWORD dwHeaderSize,
1161 IN LPCSTR lpszFileExtension,
1162 IN LPCSTR dwReserved
1165 LPURLCACHE_HEADER pHeader;
1166 CACHEFILE_ENTRY * pEntry;
1167 URL_CACHEFILE_ENTRY * pUrlEntry;
1168 DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1170 BOOL bFound = FALSE;
1171 DWORD dwOffsetLocalFileName;
1172 DWORD dwOffsetHeader;
1173 DWORD dwFileSizeLow;
1174 DWORD dwFileSizeHigh;
1177 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1178 debugstr_a(lpszUrlName),
1179 debugstr_a(lpszLocalFileName),
1188 ERR("dwReserved != 0\n");
1189 SetLastError(ERROR_INVALID_PARAMETER);
1192 if (lpHeaderInfo == NULL)
1194 FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1197 hFile = CreateFileA(lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1198 if (hFile == INVALID_HANDLE_VALUE)
1200 ERR("couldn't open file (error is %ld)\n", GetLastError());
1205 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1206 if ((dwFileSizeLow == -1) && (GetLastError() != NO_ERROR))
1208 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1215 if (!URLCache_OpenIndex())
1218 if (!(pHeader = URLCache_LockIndex()))
1221 if (URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry) || URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
1223 URLCache_UnlockIndex(pHeader);
1224 FIXME("entry already in cache - don't know what to do!\n");
1225 SetLastError(ERROR_FILE_NOT_FOUND);
1229 if (memcmp(lpszLocalFileName, szCacheContentPath, strlen(szCacheContentPath)))
1231 URLCache_UnlockIndex(pHeader);
1232 ERR("path must begin with cache content path\n");
1233 SetLastError(ERROR_INVALID_PARAMETER);
1237 lpszLocalFileName += strlen(szCacheContentPath);
1239 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1241 if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1250 URLCache_UnlockIndex(pHeader);
1251 ERR("cache directory not found in path %s\n", lpszLocalFileName);
1252 SetLastError(ERROR_INVALID_PARAMETER);
1256 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1258 dwOffsetLocalFileName = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlName) + 1); /* + 1 for NULL terminating char */
1259 dwOffsetHeader = DWORD_ALIGN(dwOffsetLocalFileName + strlen(lpszLocalFileName) + 1);
1260 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1262 if (dwBytesNeeded % BLOCKSIZE)
1264 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1265 dwBytesNeeded += BLOCKSIZE;
1268 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1270 /* we should grow the index file here */
1271 URLCache_UnlockIndex(pHeader);
1272 FIXME("no free entries\n");
1276 /* FindFirstFreeEntry fills in blocks used */
1277 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1278 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1279 pUrlEntry->CacheDir = cDirectory;
1280 pUrlEntry->CacheEntryType = CacheEntryType;
1281 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1282 pUrlEntry->dwExemptDelta = 0;
1283 pUrlEntry->dwHitRate = 0;
1284 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1285 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1286 pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1287 pUrlEntry->dwSizeHigh = 0;
1288 pUrlEntry->dwSizeLow = dwFileSizeLow;
1289 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1290 pUrlEntry->dwUseCount = 0;
1291 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1292 pUrlEntry->LastModifiedTime = LastModifiedTime;
1293 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1294 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1295 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1296 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1299 pUrlEntry->dwUnknown1 = 0;
1300 pUrlEntry->dwUnknown2 = 0;
1301 pUrlEntry->dwUnknown3 = 0x60;
1302 pUrlEntry->Unknown4 = 0;
1303 pUrlEntry->wUnknown5 = 0x1010;
1304 pUrlEntry->dwUnknown6 = 0;
1305 pUrlEntry->dwUnknown7 = 0;
1306 pUrlEntry->dwUnknown8 = 0;
1308 strcpy(pUrlEntry->szSourceUrlName, lpszUrlName);
1309 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), lpszLocalFileName);
1310 memcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetHeader), lpHeaderInfo, dwHeaderSize);
1312 if (!URLCache_AddEntryToHash(pHeader, lpszUrlName, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1314 URLCache_UnlockIndex(pHeader);
1318 URLCache_UnlockIndex(pHeader);
1323 BOOL WINAPI ReadUrlCacheEntryStream(
1324 IN HANDLE hUrlCacheStream,
1325 IN DWORD dwLocation,
1326 IN OUT LPVOID lpBuffer,
1327 IN OUT LPDWORD lpdwLen,
1331 /* Get handle to file from 'stream' */
1332 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1334 if (dwReserved != 0)
1336 ERR("dwReserved != 0\n");
1337 SetLastError(ERROR_INVALID_PARAMETER);
1341 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1343 SetLastError(ERROR_INVALID_HANDLE);
1347 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == -1)
1349 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
1352 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
1353 IN LPCSTR lpszUrlName,
1354 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1355 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1356 IN BOOL fRandomRead,
1360 /* NOTE: this is not the same as the way that the native
1361 * version allocates 'stream' handles. I did it this way
1362 * as it is much easier and no applications should depend
1363 * on this behaviour. (Native version appears to allocate
1364 * indices into a table)
1366 STREAM_HANDLE * pStream;
1369 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
1371 lpdwCacheEntryInfoBufferSize,
1377 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1382 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
1384 if (hFile == INVALID_HANDLE_VALUE)
1387 /* allocate handle storage space */
1388 pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
1392 SetLastError(ERROR_OUTOFMEMORY);
1396 pStream->hFile = hFile;
1397 strcpy(pStream->lpszUrl, lpszUrlName);
1398 return (HANDLE)pStream;
1401 BOOL WINAPI UnlockUrlCacheEntryStream(
1402 IN HANDLE hUrlCacheStream,
1406 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1408 if (dwReserved != 0)
1410 ERR("dwReserved != 0\n");
1411 SetLastError(ERROR_INVALID_PARAMETER);
1415 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1417 SetLastError(ERROR_INVALID_HANDLE);
1421 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
1424 /* close file handle */
1425 CloseHandle(pStream->hFile);
1427 /* free allocated space */
1428 HeapFree(GetProcessHeap(), 0, pStream);
1433 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
1435 LPURLCACHE_HEADER pHeader;
1436 CACHEFILE_ENTRY * pEntry;
1439 BYTE * AllocationTable;
1441 if (!URLCache_OpenIndex())
1444 if (!(pHeader = URLCache_LockIndex()))
1447 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1449 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
1451 URLCache_UnlockIndex(pHeader);
1452 TRACE("entry %s not found!\n", lpszUrlName);
1453 SetLastError(ERROR_FILE_NOT_FOUND);
1458 AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
1460 /* update allocation table */
1461 dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
1462 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
1463 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
1465 URLCache_DeleteEntry(pEntry);
1467 /* FIXME: update hash table */
1469 URLCache_UnlockIndex(pHeader);
1474 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID
1477 FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
1481 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
1482 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
1484 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
1488 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
1489 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
1491 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
1495 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
1501 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
1502 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
1506 SetLastError(ERROR_FILE_NOT_FOUND);
1510 /***********************************************************************
1511 * GetUrlCacheEntryInfoW (WININET.@)
1514 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1515 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntry,
1516 LPDWORD lpCacheEntrySize)
1518 FIXME("(%s) stub\n",debugstr_w(lpszUrl));
1519 SetLastError(ERROR_FILE_NOT_FOUND);
1523 /***********************************************************************
1524 * GetUrlCacheEntryInfoExW (WININET.@)
1527 BOOL WINAPI GetUrlCacheEntryInfoExW(
1529 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1530 LPDWORD lpdwCacheEntryInfoBufSize,
1531 LPWSTR lpszReserved,
1532 LPDWORD lpdwReserved,
1536 FIXME(" url=%s, flags=%ld\n",debugstr_w(lpszUrl),dwFlags);
1537 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
1541 /***********************************************************************
1542 * GetUrlCacheConfigInfoA (WININET.@)
1544 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
1546 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
1548 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
1549 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1553 /***********************************************************************
1554 * GetUrlCacheConfigInfoW (WININET.@)
1556 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
1558 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
1559 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1564 /***********************************************************************
1565 * SetUrlCacheEntryInfoA (WININET.@)
1567 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, DWORD dwFieldControl)
1573 /***********************************************************************
1574 * SetUrlCacheEntryInfoW (WININET.@)
1576 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)