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
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
49 #define ENTRY_START_OFFSET 0x4000
52 #define CONTENT_DIRECTORY "\\Content.IE5\\"
53 #define HASHTABLE_SIZE 448
54 #define HASHTABLE_BLOCKSIZE 7
55 #define ALLOCATION_TABLE_OFFSET 0x250
56 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
57 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
59 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
60 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
61 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
62 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
63 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
65 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x) + 3) >> 2) << 2)
67 typedef struct _CACHEFILE_ENTRY
71 DWORD dwSignature; /* e.g. "URL " */
72 /* CHAR szSignature[4];
74 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
77 typedef struct _URL_CACHEFILE_ENTRY
79 CACHEFILE_ENTRY CacheFileEntry;
80 FILETIME LastModifiedTime;
81 FILETIME LastAccessTime;
82 WORD wExpiredDate; /* expire date in dos format */
83 WORD wExpiredTime; /* expire time in dos format */
84 DWORD dwUnknown1; /* usually zero */
85 DWORD dwSizeLow; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow */
86 DWORD dwSizeHigh; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeHigh */
87 DWORD dwUnknown2; /* usually zero */
88 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
89 DWORD dwUnknown3; /* usually 0x60 */
90 DWORD dwOffsetUrl; /* usually 0x68 */
91 BYTE CacheDir; /* index of cache directory this url is stored in */
92 BYTE Unknown4; /* usually zero */
93 WORD wUnknown5; /* usually 0x1010 */
94 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
95 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
96 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
97 DWORD dwHeaderInfoSize;
98 DWORD dwUnknown6; /* usually zero */
99 WORD wLastSyncDate; /* last sync date in dos format */
100 WORD wLastSyncTime; /* last sync time in dos format */
101 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
102 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
103 WORD wUnknownDate; /* usually same as wLastSyncDate */
104 WORD wUnknownTime; /* usually same as wLastSyncTime */
105 DWORD dwUnknown7; /* usually zero */
106 DWORD dwUnknown8; /* usually zero */
107 CHAR szSourceUrlName[1]; /* start of url */
108 /* packing to dword align start of next field */
109 /* CHAR szLocalFileName[]; (local file name exluding path) */
110 /* packing to dword align start of next field */
111 /* CHAR szHeaderInfo[]; (header info) */
112 } URL_CACHEFILE_ENTRY;
120 typedef struct _HASH_CACHEFILE_ENTRY
122 CACHEFILE_ENTRY CacheFileEntry;
124 DWORD dwHashTableNumber;
125 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
126 } HASH_CACHEFILE_ENTRY;
128 typedef struct _DIRECTORY_DATA
131 char filename[DIR_LENGTH];
134 typedef struct _URLCACHE_HEADER
136 char szSignature[28];
138 DWORD dwOffsetFirstHashTable;
139 DWORD dwIndexCapacityInBlocks;
140 DWORD dwBlocksInUse; /* is this right? */
142 DWORD dwCacheLimitLow; /* disk space limit for cache */
143 DWORD dwCacheLimitHigh; /* disk space limit for cache */
144 DWORD dwUnknown4; /* current disk space usage for cache? */
145 DWORD dwUnknown5; /* current disk space usage for cache? */
146 DWORD dwUnknown6; /* possibly a flag? */
148 BYTE DirectoryCount; /* number of directory_data's */
149 BYTE Unknown8[3]; /* just padding? */
150 DIRECTORY_DATA directory_data[1]; /* first directory entry */
151 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
152 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
155 typedef struct _STREAM_HANDLE
161 /**** File Global Variables ****/
162 static HANDLE hCacheIndexMapping = NULL; /* handle to file mapping */
163 static LPSTR szCacheContentPath = NULL; /* path to content index */
164 static HANDLE hMutex = NULL;
166 /***********************************************************************
167 * URLCache_PathToObjectName (Internal)
169 * Converts a path to a name suitable for use as a Win32 object name.
170 * Replaces '\\' characters in-place with the specified character
171 * (usually '_' or '!')
177 static void URLCache_PathToObjectName(LPSTR lpszPath, char replace)
180 for (ch = *lpszPath; (ch = *lpszPath); lpszPath++)
188 #define CHAR_BIT (8 * sizeof(CHAR))
191 /***********************************************************************
192 * URLCache_Allocation_BlockIsFree (Internal)
194 * Is the specified block number free?
201 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
203 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
204 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
207 /***********************************************************************
208 * URLCache_Allocation_BlockFree (Internal)
210 * Marks the specified block as free
216 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
218 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
219 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
222 /***********************************************************************
223 * URLCache_Allocation_BlockAllocate (Internal)
225 * Marks the specified block as allocated
231 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
233 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
234 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
237 /***********************************************************************
238 * URLCache_FindEntry (Internal)
240 * Finds an entry without using the hash tables
243 * TRUE if it found the specified entry
247 static BOOL URLCache_FindEntry(LPCURLCACHE_HEADER pHeader, LPCSTR szUrl, CACHEFILE_ENTRY ** ppEntry)
249 CACHEFILE_ENTRY * pCurrentEntry;
252 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
254 for (pCurrentEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET);
255 (DWORD)((LPBYTE)pCurrentEntry - (LPBYTE)pHeader) < pHeader->dwFileSize;
256 pCurrentEntry = (CACHEFILE_ENTRY *)((LPBYTE)pCurrentEntry + pCurrentEntry->dwBlocksUsed * BLOCKSIZE))
258 dwBlockNumber = (DWORD)((LPBYTE)pCurrentEntry - (LPBYTE)pHeader - ENTRY_START_OFFSET) / BLOCKSIZE;
259 while (URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber))
261 if (dwBlockNumber >= pHeader->dwIndexCapacityInBlocks)
264 pCurrentEntry = (CACHEFILE_ENTRY *)((LPBYTE)pCurrentEntry + BLOCKSIZE);
265 dwBlockNumber = (DWORD)((LPBYTE)pCurrentEntry - (LPBYTE)pHeader - ENTRY_START_OFFSET) / BLOCKSIZE;
268 switch (pCurrentEntry->dwSignature)
270 case URL_SIGNATURE: /* "URL " */
271 case LEAK_SIGNATURE: /* "LEAK" */
273 URL_CACHEFILE_ENTRY * pUrlEntry = (URL_CACHEFILE_ENTRY *)pCurrentEntry;
274 if (!strcmp(szUrl, pUrlEntry->szSourceUrlName))
276 *ppEntry = pCurrentEntry;
277 /* FIXME: should we update the LastAccessTime here? */
282 case HASH_SIGNATURE: /* HASH entries parsed in FindEntryInHash */
283 case 0xDEADBEEF: /* this is always at offset 0x4000 in URL cache for some reason */
286 FIXME("Unknown entry %.4s ignored\n", (LPCSTR)&pCurrentEntry->dwSignature);
293 /***********************************************************************
294 * URLCache_OpenIndex (Internal)
296 * Opens the index file and saves mapping handle in hCacheIndexMapping
303 static BOOL URLCache_OpenIndex()
306 CHAR szFullPath[MAX_PATH];
307 CHAR szFileMappingName[MAX_PATH+10];
308 CHAR szMutexName[MAX_PATH+1];
311 if (!szCacheContentPath)
313 szCacheContentPath = (LPSTR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(CHAR));
314 *szCacheContentPath = '\0';
317 if (*szCacheContentPath == '\0')
319 if (FAILED(SHGetSpecialFolderPathA(NULL, szCacheContentPath, CSIDL_INTERNET_CACHE, TRUE)))
321 strcat(szCacheContentPath, CONTENT_DIRECTORY);
324 strcpy(szFullPath, szCacheContentPath);
325 strcat(szFullPath, "index.dat");
327 if (hCacheIndexMapping)
330 hFile = CreateFileA(szFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
331 if (hFile == INVALID_HANDLE_VALUE)
333 FIXME("need to create cache index file\n");
337 dwFileSize = GetFileSize(hFile, NULL);
338 if (dwFileSize == INVALID_FILE_SIZE)
343 FIXME("need to create cache index file\n");
347 strcpy(szFileMappingName, szFullPath);
348 sprintf(szFileMappingName + strlen(szFileMappingName), "\\%lu", dwFileSize);
349 URLCache_PathToObjectName(szFileMappingName, '_');
350 hCacheIndexMapping = OpenFileMappingA(FILE_MAP_WRITE, FALSE, szFileMappingName);
351 if (!hCacheIndexMapping)
352 hCacheIndexMapping = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, 0, 0, szFileMappingName);
354 if (!hCacheIndexMapping)
356 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
360 strcpy(szMutexName, szFullPath);
361 CharLowerA(szMutexName);
362 URLCache_PathToObjectName(szMutexName, '!');
363 strcat(szMutexName, "!");
365 if ((hMutex = CreateMutexA(NULL, FALSE, szMutexName)) == NULL)
367 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
368 CloseHandle(hCacheIndexMapping);
375 /***********************************************************************
376 * URLCache_CloseIndex (Internal)
384 #if 0 /* not used at the moment */
385 static BOOL URLCache_CloseIndex()
387 return CloseHandle(hCacheIndexMapping);
391 /***********************************************************************
392 * URLCache_FindFirstFreeEntry (Internal)
394 * Finds and allocates the first block of free space big enough and
395 * sets ppEntry to point to it.
398 * TRUE if it had enough space
399 * FALSE if it couldn't find enough space
402 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
404 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
407 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
409 for (dwFreeCounter = 0;
410 dwFreeCounter < dwBlocksNeeded &&
411 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
412 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
414 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
416 if (dwFreeCounter == dwBlocksNeeded)
419 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
420 for (index = 0; index < dwBlocksNeeded; index++)
421 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
422 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
423 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
430 /***********************************************************************
431 * URLCache_DeleteEntry (Internal)
433 * Deletes the specified entry and frees the space allocated to it
436 * TRUE if it succeeded
440 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
442 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
446 /***********************************************************************
447 * URLCache_LockIndex (Internal)
450 static LPURLCACHE_HEADER URLCache_LockIndex()
453 LPVOID pIndexData = MapViewOfFile(hCacheIndexMapping, FILE_MAP_WRITE, 0, 0, 0);
454 URLCACHE_HEADER * pHeader = (URLCACHE_HEADER *)pIndexData;
458 TRACE("Signature: %s, file size: %ld bytes\n", pHeader->szSignature, pHeader->dwFileSize);
460 for (index = 0; index < pHeader->DirectoryCount; index++)
462 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
466 WaitForSingleObject(hMutex, INFINITE);
471 /***********************************************************************
472 * URLCache_UnlockIndex (Internal)
475 static BOOL URLCache_UnlockIndex(LPURLCACHE_HEADER pHeader)
478 ReleaseMutex(hMutex);
479 return UnmapViewOfFile(pHeader);
482 /***********************************************************************
483 * URLCache_LocalFileNameToPath (Internal)
485 * Copies the full path to the specified buffer given the local file
486 * name and the index of the directory it is in. Always sets value in
487 * lpBufferSize to the required buffer size.
490 * TRUE if the buffer was big enough
491 * FALSE if the buffer was too small
494 static BOOL URLCache_LocalFileNameToPath(LPCURLCACHE_HEADER pHeader, LPCSTR szLocalFileName, BYTE Directory, LPSTR szPath, LPLONG lpBufferSize)
497 if (Directory >= pHeader->DirectoryCount)
503 nRequired = (strlen(szCacheContentPath) + DIR_LENGTH + strlen(szLocalFileName) + 1) * sizeof(CHAR);
504 if (nRequired < *lpBufferSize)
506 strcpy(szPath, szCacheContentPath);
507 strncat(szPath, pHeader->directory_data[Directory].filename, DIR_LENGTH);
508 strcat(szPath, "\\");
509 strcat(szPath, szLocalFileName);
510 *lpBufferSize = nRequired;
513 *lpBufferSize = nRequired;
517 /***********************************************************************
518 * URLCache_CopyEntry (Internal)
520 * Copies an entry from the cache index file to the Win32 structure
523 * TRUE if the buffer was big enough
524 * FALSE if the buffer was too small
527 static BOOL URLCache_CopyEntry(LPCURLCACHE_HEADER pHeader, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, LPDWORD lpdwBufferSize, URL_CACHEFILE_ENTRY * pUrlEntry)
529 int lenUrl = strlen(pUrlEntry->szSourceUrlName);
530 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
531 LONG nLocalFilePathSize;
532 LPSTR lpszLocalFileName;
534 if (*lpdwBufferSize >= dwRequiredSize)
536 lpCacheEntryInfo->lpHeaderInfo = NULL;
537 lpCacheEntryInfo->lpszFileExtension = NULL;
538 lpCacheEntryInfo->lpszLocalFileName = NULL;
539 lpCacheEntryInfo->lpszSourceUrlName = NULL;
540 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
541 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
542 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
543 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
544 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->dwSizeHigh;
545 lpCacheEntryInfo->dwSizeLow = pUrlEntry->dwSizeLow;
546 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
547 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
548 DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
549 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
550 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
551 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
552 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
553 DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
556 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
557 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
558 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
559 dwRequiredSize += lenUrl + 1;
561 if (*lpdwBufferSize >= dwRequiredSize)
563 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrl - 1;
564 strcpy(lpCacheEntryInfo->lpszSourceUrlName, pUrlEntry->szSourceUrlName);
567 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
568 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
569 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
571 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
572 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
573 if (URLCache_LocalFileNameToPath(pHeader, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize))
575 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
577 dwRequiredSize += nLocalFilePathSize;
579 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
580 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
581 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
582 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
584 if (*lpdwBufferSize >= dwRequiredSize)
586 lpCacheEntryInfo->lpHeaderInfo = (LPSTR)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
587 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
588 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
590 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
591 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
592 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
594 if (dwRequiredSize > *lpdwBufferSize)
596 *lpdwBufferSize = dwRequiredSize;
597 SetLastError(ERROR_INSUFFICIENT_BUFFER);
600 *lpdwBufferSize = dwRequiredSize;
604 /***********************************************************************
605 * URLCache_HashKey (Internal)
607 * Returns the hash key for a given string
610 * hash key for the string
613 static DWORD URLCache_HashKey(LPCSTR lpszKey)
615 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
616 * but the algorithm and result are not the same!
618 static const unsigned char lookupTable[256] =
620 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
621 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
622 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
623 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
624 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
625 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
626 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
627 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
628 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
629 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
630 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
631 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
632 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
633 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
634 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
635 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
636 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
637 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
638 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
639 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
640 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
641 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
642 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
643 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
644 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
645 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
646 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
647 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
648 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
649 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
650 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
651 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
655 int subscript[sizeof(key) / sizeof(key[0])];
657 subscript[0] = *lpszKey;
658 subscript[1] = (char)(*lpszKey + 1);
659 subscript[2] = (char)(*lpszKey + 2);
660 subscript[3] = (char)(*lpszKey + 3);
662 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
663 key[i] = lookupTable[i];
665 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
667 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
668 key[i] = lookupTable[*lpszKey ^ key[i]];
671 return *(DWORD *)key;
674 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
676 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
679 /***********************************************************************
680 * URLCache_FindEntryInHash (Internal)
682 * Searches all the hash tables in the index for the given URL and
683 * returns the entry, if it was found, in ppEntry
686 * TRUE if the entry was found
687 * FALSE if the entry could not be found
690 static BOOL URLCache_FindEntryInHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, CACHEFILE_ENTRY ** ppEntry)
692 /* structure of hash table:
693 * 448 entries divided into 64 blocks
694 * each block therefore contains a chain of 7 key/offset pairs
695 * how position in table is calculated:
696 * 1. the url is hashed in helper function
697 * 2. the key % 64 * 8 is the offset
698 * 3. the key in the hash table is the hash key aligned to 64
701 * there can be multiple hash tables in the file and the offset to
702 * the next one is stored in the header of the hash table
704 DWORD key = URLCache_HashKey(lpszUrl);
705 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
706 HASH_CACHEFILE_ENTRY * pHashEntry;
707 DWORD dwHashTableNumber = 0;
709 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
711 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
712 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
713 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
716 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
718 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
721 /* make sure that it is in fact a hash entry */
722 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
724 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
728 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
730 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
731 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
733 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashElement->dwOffsetEntry);
741 /***********************************************************************
742 * URLCache_HashEntrySetUse (Internal)
744 * Searches all the hash tables in the index for the given URL and
745 * sets the use count (stored or'ed with key)
748 * TRUE if the entry was found
749 * FALSE if the entry could not be found
752 static BOOL URLCache_HashEntrySetUse(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwUseCount)
754 /* see URLCache_FindEntryInHash for structure of hash tables */
756 DWORD key = URLCache_HashKey(lpszUrl);
757 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
758 HASH_CACHEFILE_ENTRY * pHashEntry;
759 DWORD dwHashTableNumber = 0;
761 if (dwUseCount >= HASHTABLE_NUM_ENTRIES)
763 ERR("don't know what to do when use count exceeds %d, guessing\n", HASHTABLE_NUM_ENTRIES);
764 dwUseCount = HASHTABLE_NUM_ENTRIES - 1;
767 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_BLOCKSIZE;
769 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
770 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
771 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
774 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
776 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
779 /* make sure that it is in fact a hash entry */
780 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
782 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
786 for (i = 0; i < 7; i++)
788 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
789 if (key == (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
791 pHashElement->dwOffsetEntry = dwUseCount | (DWORD)(pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
799 /***********************************************************************
800 * URLCache_AddEntryToHash (Internal)
802 * Searches all the hash tables for a free slot based on the offset
803 * generated from the hash key. If a free slot is found, the offset and
804 * key are entered into the hash table.
807 * TRUE if the entry was added
808 * FALSE if the entry could not be added
811 static BOOL URLCache_AddEntryToHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
813 /* see URLCache_FindEntryInHash for structure of hash tables */
815 DWORD key = URLCache_HashKey(lpszUrl);
816 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
817 HASH_CACHEFILE_ENTRY * pHashEntry;
818 DWORD dwHashTableNumber = 0;
820 key = (DWORD)(key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
822 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
823 ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) >= ENTRY_START_OFFSET) && ((DWORD)((LPBYTE)pHashEntry - (LPBYTE)pHeader) < pHeader->dwFileSize);
824 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
827 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
829 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
832 /* make sure that it is in fact a hash entry */
833 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
835 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
839 for (i = 0; i < 7; i++)
841 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
842 if (pHashElement->dwHashKey == 3 /* FIXME: just 3? */) /* if the slot is free */
844 pHashElement->dwHashKey = key;
845 pHashElement->dwOffsetEntry = dwOffsetEntry;
850 FIXME("need to create another hash table\n");
854 /***********************************************************************
855 * GetUrlCacheEntryInfoExA (WININET.@)
858 BOOL WINAPI GetUrlCacheEntryInfoExA(
860 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
861 LPDWORD lpdwCacheEntryInfoBufSize,
863 LPDWORD lpdwReserved,
867 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
870 lpdwCacheEntryInfoBufSize,
876 if ((lpszReserved != NULL) ||
877 (lpdwReserved != NULL) ||
878 (lpReserved != NULL))
880 ERR("Reserved value was not 0\n");
881 SetLastError(ERROR_INVALID_PARAMETER);
885 FIXME("Undocumented flag(s): %lx\n", dwFlags);
886 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
889 /***********************************************************************
890 * GetUrlCacheEntryInfoA (WININET.@)
893 BOOL WINAPI GetUrlCacheEntryInfoA(
894 IN LPCSTR lpszUrlName,
895 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
896 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
899 LPURLCACHE_HEADER pHeader;
900 CACHEFILE_ENTRY * pEntry;
901 URL_CACHEFILE_ENTRY * pUrlEntry;
903 if (!URLCache_OpenIndex())
906 if (!(pHeader = URLCache_LockIndex()))
909 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
911 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
913 URLCache_UnlockIndex(pHeader);
914 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
915 SetLastError(ERROR_FILE_NOT_FOUND);
920 /* FIXME: check signature */
922 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
923 TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
924 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
926 if (!URLCache_CopyEntry(pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry))
928 URLCache_UnlockIndex(pHeader);
931 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
933 URLCache_UnlockIndex(pHeader);
938 /***********************************************************************
939 * RetrieveUrlCacheEntryFileA (WININET.@)
942 BOOL WINAPI RetrieveUrlCacheEntryFileA(
943 IN LPCSTR lpszUrlName,
944 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
945 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
949 LPURLCACHE_HEADER pHeader;
950 CACHEFILE_ENTRY * pEntry;
951 URL_CACHEFILE_ENTRY * pUrlEntry;
953 if (!URLCache_OpenIndex())
956 if (!(pHeader = URLCache_LockIndex()))
959 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
961 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
963 URLCache_UnlockIndex(pHeader);
964 TRACE("entry %s not found!\n", lpszUrlName);
965 SetLastError(ERROR_FILE_NOT_FOUND);
970 /* FIXME: check signature */
972 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
973 TRACE("Found URL: %s\n", pUrlEntry->szSourceUrlName);
974 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
976 pUrlEntry->dwHitRate++;
977 pUrlEntry->dwUseCount++;
978 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
980 if (!URLCache_CopyEntry(pHeader, lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize, pUrlEntry))
982 URLCache_UnlockIndex(pHeader);
985 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
987 URLCache_UnlockIndex(pHeader);
992 /***********************************************************************
993 * UnlockUrlCacheEntryFileA (WININET.@)
996 BOOL WINAPI UnlockUrlCacheEntryFileA(
997 IN LPCSTR lpszUrlName,
1001 LPURLCACHE_HEADER pHeader;
1002 CACHEFILE_ENTRY * pEntry;
1003 URL_CACHEFILE_ENTRY * pUrlEntry;
1007 ERR("dwReserved != 0\n");
1008 SetLastError(ERROR_INVALID_PARAMETER);
1012 if (!URLCache_OpenIndex())
1015 if (!(pHeader = URLCache_LockIndex()))
1018 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1020 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
1022 URLCache_UnlockIndex(pHeader);
1023 TRACE("entry %s not found!\n", lpszUrlName);
1024 SetLastError(ERROR_FILE_NOT_FOUND);
1029 /* FIXME: check signature */
1031 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1033 if (pUrlEntry->dwUseCount == 0)
1035 URLCache_UnlockIndex(pHeader);
1038 pUrlEntry->dwUseCount--;
1039 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1041 URLCache_UnlockIndex(pHeader);
1046 /***********************************************************************
1047 * CreateUrlCacheEntryA (WININET.@)
1050 BOOL WINAPI CreateUrlCacheEntryA(
1051 IN LPCSTR lpszUrlName,
1052 IN DWORD dwExpectedFileSize,
1053 IN LPCSTR lpszFileExtension,
1054 OUT LPSTR lpszFileName,
1058 LPURLCACHE_HEADER pHeader;
1059 CHAR szFile[MAX_PATH];
1060 CHAR szExtension[MAX_PATH];
1063 LPCSTR lpszFileNameExtension;
1064 LPSTR lpszFileNameNoPath;
1066 int countnoextension;
1068 LONG lBufferSize = MAX_PATH * sizeof(CHAR);
1069 BOOL bFound = FALSE;
1074 ERR("dwReserved != 0\n");
1075 SetLastError(ERROR_INVALID_PARAMETER);
1079 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1082 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1085 for (lpszUrlPart = lpszUrlEnd;
1086 (lpszUrlPart >= lpszUrlName);
1089 if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1096 if (!strcmp(lpszUrlPart, "www"))
1098 lpszUrlPart += strlen("www");
1101 count = lpszUrlEnd - lpszUrlPart;
1103 if (bFound && (count < MAX_PATH))
1105 memcpy(szFile, lpszUrlPart, count * sizeof(CHAR));
1106 szFile[count] = '\0';
1107 /* FIXME: get rid of illegal characters like \, / and : */
1111 FIXME("need to generate a random filename");
1114 TRACE("File name: %s\n", szFile);
1116 if (!URLCache_OpenIndex())
1119 if (!(pHeader = URLCache_LockIndex()))
1122 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
1124 URLCache_LocalFileNameToPath(pHeader, szFile, CacheDir, lpszFileName, &lBufferSize);
1126 URLCache_UnlockIndex(pHeader);
1128 lpszFileNameNoPath = lpszFileName + strlen(szCacheContentPath) + DIR_LENGTH + 1;
1130 countnoextension = strlen(lpszFileNameNoPath);
1131 lpszFileNameExtension = PathFindExtensionA(lpszFileNameNoPath);
1132 if (lpszFileNameExtension)
1133 countnoextension -= strlen(lpszFileNameExtension);
1134 *szExtension = '\0';
1136 if (lpszFileExtension)
1138 szExtension[0] = '.';
1139 strcpy(szExtension+1, lpszFileExtension);
1142 for (i = 0; i < 255; i++)
1145 strncpy(lpszFileNameNoPath, szFile, countnoextension);
1146 sprintf(lpszFileNameNoPath + countnoextension, "[%u]%s", i, szExtension);
1147 TRACE("Trying: %s\n", lpszFileName);
1148 hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
1149 if (hFile != INVALID_HANDLE_VALUE)
1159 /***********************************************************************
1160 * CommitUrlCacheEntryA (WININET.@)
1163 BOOL WINAPI CommitUrlCacheEntryA(
1164 IN LPCSTR lpszUrlName,
1165 IN LPCSTR lpszLocalFileName,
1166 IN FILETIME ExpireTime,
1167 IN FILETIME LastModifiedTime,
1168 IN DWORD CacheEntryType,
1169 IN LPBYTE lpHeaderInfo,
1170 IN DWORD dwHeaderSize,
1171 IN LPCSTR lpszFileExtension,
1172 IN LPCSTR dwReserved
1175 LPURLCACHE_HEADER pHeader;
1176 CACHEFILE_ENTRY * pEntry;
1177 URL_CACHEFILE_ENTRY * pUrlEntry;
1178 DWORD dwBytesNeeded = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1180 BOOL bFound = FALSE;
1181 DWORD dwOffsetLocalFileName;
1182 DWORD dwOffsetHeader;
1183 DWORD dwFileSizeLow;
1184 DWORD dwFileSizeHigh;
1187 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1188 debugstr_a(lpszUrlName),
1189 debugstr_a(lpszLocalFileName),
1198 ERR("dwReserved != 0\n");
1199 SetLastError(ERROR_INVALID_PARAMETER);
1202 if (lpHeaderInfo == NULL)
1204 FIXME("lpHeaderInfo == NULL - will crash at the moment\n");
1207 hFile = CreateFileA(lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1208 if (hFile == INVALID_HANDLE_VALUE)
1210 ERR("couldn't open file (error is %ld)\n", GetLastError());
1215 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1216 if ((dwFileSizeLow == -1) && (GetLastError() != NO_ERROR))
1218 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1225 if (!URLCache_OpenIndex())
1228 if (!(pHeader = URLCache_LockIndex()))
1231 if (URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry) || URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
1233 URLCache_UnlockIndex(pHeader);
1234 FIXME("entry already in cache - don't know what to do!\n");
1235 SetLastError(ERROR_FILE_NOT_FOUND);
1239 if (memcmp(lpszLocalFileName, szCacheContentPath, strlen(szCacheContentPath)))
1241 URLCache_UnlockIndex(pHeader);
1242 ERR("path must begin with cache content path\n");
1243 SetLastError(ERROR_INVALID_PARAMETER);
1247 lpszLocalFileName += strlen(szCacheContentPath);
1249 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1251 if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1260 URLCache_UnlockIndex(pHeader);
1261 ERR("cache directory not found in path %s\n", lpszLocalFileName);
1262 SetLastError(ERROR_INVALID_PARAMETER);
1266 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
1268 dwOffsetLocalFileName = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlName) + 1); /* + 1 for NULL terminating char */
1269 dwOffsetHeader = DWORD_ALIGN(dwOffsetLocalFileName + strlen(lpszLocalFileName) + 1);
1270 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
1272 if (dwBytesNeeded % BLOCKSIZE)
1274 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
1275 dwBytesNeeded += BLOCKSIZE;
1278 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
1280 /* we should grow the index file here */
1281 URLCache_UnlockIndex(pHeader);
1282 FIXME("no free entries\n");
1286 /* FindFirstFreeEntry fills in blocks used */
1287 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1288 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
1289 pUrlEntry->CacheDir = cDirectory;
1290 pUrlEntry->CacheEntryType = CacheEntryType;
1291 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
1292 pUrlEntry->dwExemptDelta = 0;
1293 pUrlEntry->dwHitRate = 0;
1294 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
1295 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
1296 pUrlEntry->dwOffsetUrl = sizeof(*pUrlEntry) - sizeof(pUrlEntry->szSourceUrlName);
1297 pUrlEntry->dwSizeHigh = 0;
1298 pUrlEntry->dwSizeLow = dwFileSizeLow;
1299 pUrlEntry->dwSizeHigh = dwFileSizeHigh;
1300 pUrlEntry->dwUseCount = 0;
1301 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
1302 pUrlEntry->LastModifiedTime = LastModifiedTime;
1303 FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1304 FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1305 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
1306 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
1309 pUrlEntry->dwUnknown1 = 0;
1310 pUrlEntry->dwUnknown2 = 0;
1311 pUrlEntry->dwUnknown3 = 0x60;
1312 pUrlEntry->Unknown4 = 0;
1313 pUrlEntry->wUnknown5 = 0x1010;
1314 pUrlEntry->dwUnknown6 = 0;
1315 pUrlEntry->dwUnknown7 = 0;
1316 pUrlEntry->dwUnknown8 = 0;
1318 strcpy(pUrlEntry->szSourceUrlName, lpszUrlName);
1319 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), lpszLocalFileName);
1320 memcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetHeader), lpHeaderInfo, dwHeaderSize);
1322 if (!URLCache_AddEntryToHash(pHeader, lpszUrlName, (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader)))
1324 URLCache_UnlockIndex(pHeader);
1328 URLCache_UnlockIndex(pHeader);
1333 BOOL WINAPI ReadUrlCacheEntryStream(
1334 IN HANDLE hUrlCacheStream,
1335 IN DWORD dwLocation,
1336 IN OUT LPVOID lpBuffer,
1337 IN OUT LPDWORD lpdwLen,
1341 /* Get handle to file from 'stream' */
1342 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1344 if (dwReserved != 0)
1346 ERR("dwReserved != 0\n");
1347 SetLastError(ERROR_INVALID_PARAMETER);
1351 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1353 SetLastError(ERROR_INVALID_HANDLE);
1357 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == -1)
1359 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
1362 /***********************************************************************
1363 * RetrieveUrlCacheEntryStreamA (WININET.@)
1366 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
1367 IN LPCSTR lpszUrlName,
1368 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1369 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1370 IN BOOL fRandomRead,
1374 /* NOTE: this is not the same as the way that the native
1375 * version allocates 'stream' handles. I did it this way
1376 * as it is much easier and no applications should depend
1377 * on this behaviour. (Native version appears to allocate
1378 * indices into a table)
1380 STREAM_HANDLE * pStream;
1383 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
1385 lpdwCacheEntryInfoBufferSize,
1391 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1396 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
1398 if (hFile == INVALID_HANDLE_VALUE)
1401 /* allocate handle storage space */
1402 pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
1406 SetLastError(ERROR_OUTOFMEMORY);
1410 pStream->hFile = hFile;
1411 strcpy(pStream->lpszUrl, lpszUrlName);
1412 return (HANDLE)pStream;
1415 /***********************************************************************
1416 * UnlockUrlCacheEntryStream (WININET.@)
1419 BOOL WINAPI UnlockUrlCacheEntryStream(
1420 IN HANDLE hUrlCacheStream,
1424 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1426 if (dwReserved != 0)
1428 ERR("dwReserved != 0\n");
1429 SetLastError(ERROR_INVALID_PARAMETER);
1433 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1435 SetLastError(ERROR_INVALID_HANDLE);
1439 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
1442 /* close file handle */
1443 CloseHandle(pStream->hFile);
1445 /* free allocated space */
1446 HeapFree(GetProcessHeap(), 0, pStream);
1452 /***********************************************************************
1453 * DeleteUrlCacheEntryA (WININET.@)
1456 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
1458 LPURLCACHE_HEADER pHeader;
1459 CACHEFILE_ENTRY * pEntry;
1462 BYTE * AllocationTable;
1464 if (!URLCache_OpenIndex())
1467 if (!(pHeader = URLCache_LockIndex()))
1470 if (!URLCache_FindEntryInHash(pHeader, lpszUrlName, &pEntry))
1472 if (!URLCache_FindEntry(pHeader, lpszUrlName, &pEntry))
1474 URLCache_UnlockIndex(pHeader);
1475 TRACE("entry %s not found!\n", lpszUrlName);
1476 SetLastError(ERROR_FILE_NOT_FOUND);
1481 AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
1483 /* update allocation table */
1484 dwStartBlock = ((DWORD)pEntry - (DWORD)pHeader) / BLOCKSIZE;
1485 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
1486 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
1488 URLCache_DeleteEntry(pEntry);
1490 /* FIXME: update hash table */
1492 URLCache_UnlockIndex(pHeader);
1497 /***********************************************************************
1498 * CreateUrlCacheGroup (WININET.@)
1501 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID
1504 FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
1508 /***********************************************************************
1509 * FindFirstUrlCacheEntryA (WININET.@)
1512 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
1513 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
1515 FIXME("(%s, %p, %p): stub\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
1519 /***********************************************************************
1520 * FindFirstUrlCacheEntryW (WININET.@)
1523 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
1524 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
1526 FIXME("(%s, %p, %p): stub\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
1530 /***********************************************************************
1531 * DeleteUrlCacheGroup (WININET.@)
1534 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
1540 /***********************************************************************
1541 * SetUrlCacheEntryGroup (WININET.@)
1544 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
1545 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
1549 SetLastError(ERROR_FILE_NOT_FOUND);
1553 /***********************************************************************
1554 * GetUrlCacheEntryInfoW (WININET.@)
1557 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1558 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntry,
1559 LPDWORD lpCacheEntrySize)
1561 FIXME("(%s) stub\n",debugstr_w(lpszUrl));
1562 SetLastError(ERROR_FILE_NOT_FOUND);
1566 /***********************************************************************
1567 * GetUrlCacheEntryInfoExW (WININET.@)
1570 BOOL WINAPI GetUrlCacheEntryInfoExW(
1572 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1573 LPDWORD lpdwCacheEntryInfoBufSize,
1574 LPWSTR lpszReserved,
1575 LPDWORD lpdwReserved,
1579 FIXME(" url=%s, flags=%ld\n",debugstr_w(lpszUrl),dwFlags);
1580 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
1584 /***********************************************************************
1585 * GetUrlCacheConfigInfoA (WININET.@)
1587 * CacheInfo is some CACHE_CONFIG_INFO structure, with no MS info found by google
1589 BOOL WINAPI GetUrlCacheConfigInfoA(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
1591 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
1592 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1596 /***********************************************************************
1597 * GetUrlCacheConfigInfoW (WININET.@)
1599 BOOL WINAPI GetUrlCacheConfigInfoW(LPDWORD CacheInfo, LPDWORD size, DWORD bitmask)
1601 FIXME("(%p, %p, %lx)\n", CacheInfo, size, bitmask);
1602 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1607 /***********************************************************************
1608 * SetUrlCacheEntryInfoA (WININET.@)
1610 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, DWORD dwFieldControl)
1616 /***********************************************************************
1617 * SetUrlCacheEntryInfoW (WININET.@)
1619 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)