Fixed some issues found by winapi_check.
[wine/multimedia.git] / dlls / wininet / urlcache.c
blobc20a86cf92908d5228098de5ac0354086a207276
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
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
26 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winuser.h"
36 #include "wininet.h"
37 #include "winerror.h"
38 #include "internet.h"
39 #include "winreg.h"
40 #include "shlwapi.h"
41 #include "shlobj.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
47 #define ENTRY_START_OFFSET 0x4000
48 #define DIR_LENGTH 8
49 #define BLOCKSIZE 128
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
67 /* union
68 {*/
69 DWORD dwSignature; /* e.g. "URL " */
70 /* CHAR szSignature[4];
71 };*/
72 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
73 } CACHEFILE_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;
112 struct _HASH_ENTRY
114 DWORD dwHashKey;
115 DWORD dwOffsetEntry;
118 typedef struct _HASH_CACHEFILE_ENTRY
120 CACHEFILE_ENTRY CacheFileEntry;
121 DWORD dwAddressNext;
122 DWORD dwHashTableNumber;
123 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
124 } HASH_CACHEFILE_ENTRY;
126 typedef struct _DIRECTORY_DATA
128 DWORD dwUnknown;
129 char filename[DIR_LENGTH];
130 } DIRECTORY_DATA;
132 typedef struct _URLCACHE_HEADER
134 char szSignature[28];
135 DWORD dwFileSize;
136 DWORD dwOffsetFirstHashTable;
137 DWORD dwIndexCapacityInBlocks;
138 DWORD dwBlocksInUse; /* is this right? */
139 DWORD dwUnknown1;
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? */
145 DWORD dwUnknown7;
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
155 HANDLE hFile;
156 CHAR lpszUrl[1];
157 } 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 '!')
171 * RETURNS
172 * nothing
175 static void URLCache_PathToObjectName(LPSTR lpszPath, char replace)
177 char ch;
178 for (ch = *lpszPath; (ch = *lpszPath); lpszPath++)
180 if (ch == '\\')
181 *lpszPath = replace;
185 #ifndef CHAR_BIT
186 #define CHAR_BIT (8 * sizeof(CHAR))
187 #endif
189 /***********************************************************************
190 * URLCache_Allocation_BlockIsFree (Internal)
192 * Is the specified block number free?
194 * RETURNS
195 * zero if free
196 * non-zero otherwise
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
210 * RETURNS
211 * nothing
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
225 * RETURNS
226 * nothing
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
240 * RETURNS
241 * TRUE if it found the specified entry
242 * FALSE otherwise
245 static BOOL URLCache_FindEntry(LPCURLCACHE_HEADER pHeader, LPCSTR szUrl, CACHEFILE_ENTRY ** ppEntry)
247 CACHEFILE_ENTRY * pCurrentEntry;
248 DWORD dwBlockNumber;
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)
260 return FALSE;
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? */
276 return TRUE;
279 break;
280 case HASH_SIGNATURE: /* HASH entries parsed in FindEntryInHash */
281 case 0xDEADBEEF: /* this is always at offset 0x4000 in URL cache for some reason */
282 break;
283 default:
284 FIXME("Unknown entry %.4s ignored\n", (LPCSTR)&pCurrentEntry->dwSignature);
288 return FALSE;
291 /***********************************************************************
292 * URLCache_OpenIndex (Internal)
294 * Opens the index file and saves mapping handle in hCacheIndexMapping
296 * RETURNS
297 * TRUE if succeeded
298 * FALSE if failed
301 static BOOL URLCache_OpenIndex()
303 HANDLE hFile;
304 CHAR szFullPath[MAX_PATH];
305 CHAR szFileMappingName[MAX_PATH+10];
306 CHAR szMutexName[MAX_PATH+1];
307 DWORD dwFileSize;
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)))
318 return FALSE;
319 strcat(szCacheContentPath, CONTENT_DIRECTORY);
322 strcpy(szFullPath, szCacheContentPath);
323 strcat(szFullPath, "index.dat");
325 if (hCacheIndexMapping)
326 return TRUE;
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");
332 return FALSE;
335 dwFileSize = GetFileSize(hFile, NULL);
336 if (dwFileSize == INVALID_FILE_SIZE)
337 return FALSE;
339 if (dwFileSize == 0)
341 FIXME("need to create cache index file\n");
342 return FALSE;
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);
351 CloseHandle(hFile);
352 if (!hCacheIndexMapping)
354 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
355 return FALSE;
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);
367 return FALSE;
370 return TRUE;
373 /***********************************************************************
374 * URLCache_CloseIndex (Internal)
376 * Closes the index
378 * RETURNS
379 * nothing
382 #if 0 /* not used at the moment */
383 static BOOL URLCache_CloseIndex()
385 return CloseHandle(hCacheIndexMapping);
387 #endif
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.
395 * RETURNS
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;
403 DWORD dwBlockNumber;
404 DWORD dwFreeCounter;
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);
411 dwFreeCounter++)
412 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
414 if (dwFreeCounter == dwBlocksNeeded)
416 DWORD index;
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;
422 return TRUE;
425 return FALSE;
428 /***********************************************************************
429 * URLCache_DeleteEntry (Internal)
431 * Deletes the specified entry and frees the space allocated to it
433 * RETURNS
434 * TRUE if it succeeded
435 * FALSE if it failed
438 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
440 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
441 return TRUE;
444 /***********************************************************************
445 * URLCache_LockIndex (Internal)
448 static LPURLCACHE_HEADER URLCache_LockIndex()
450 BYTE index;
451 LPVOID pIndexData = MapViewOfFile(hCacheIndexMapping, FILE_MAP_WRITE, 0, 0, 0);
452 URLCACHE_HEADER * pHeader = (URLCACHE_HEADER *)pIndexData;
453 if (!pIndexData)
454 return FALSE;
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);
463 /* acquire mutex */
464 WaitForSingleObject(hMutex, INFINITE);
466 return pHeader;
469 /***********************************************************************
470 * URLCache_UnlockIndex (Internal)
473 static BOOL URLCache_UnlockIndex(LPURLCACHE_HEADER pHeader)
475 /* release mutex */
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.
487 * RETURNS
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)
494 LONG nRequired;
495 if (Directory >= pHeader->DirectoryCount)
497 *lpBufferSize = 0;
498 return FALSE;
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;
509 return TRUE;
511 *lpBufferSize = nRequired;
512 return FALSE;
515 /***********************************************************************
516 * URLCache_CopyEntry (Internal)
518 * Copies an entry from the cache index file to the Win32 structure
520 * RETURNS
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);
596 return FALSE;
598 *lpdwBufferSize = dwRequiredSize;
599 return TRUE;
602 /***********************************************************************
603 * URLCache_HashKey (Internal)
605 * Returns the hash key for a given string
607 * RETURNS
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
651 BYTE key[4];
652 int i;
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
683 * RETURNS
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
698 * note:
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))
713 int i;
714 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
716 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
717 continue;
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);
723 continue;
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);
732 return TRUE;
736 return FALSE;
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)
745 * RETURNS
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))
771 int i;
772 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
774 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
775 continue;
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);
781 continue;
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;
790 return TRUE;
794 return FALSE;
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.
804 * RETURNS
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))
824 int i;
825 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
827 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
828 break;
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);
834 break;
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;
844 return TRUE;
848 FIXME("need to create another hash table\n");
849 return FALSE;
852 /***********************************************************************
853 * GetUrlCacheEntryInfoExA (WININET.@)
856 BOOL WINAPI GetUrlCacheEntryInfoExA(
857 LPCSTR lpszUrl,
858 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
859 LPDWORD lpdwCacheEntryInfoBufSize,
860 LPSTR lpszReserved,
861 LPDWORD lpdwReserved,
862 LPVOID lpReserved,
863 DWORD dwFlags)
865 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
866 debugstr_a(lpszUrl),
867 lpCacheEntryInfo,
868 lpdwCacheEntryInfoBufSize,
869 lpszReserved,
870 lpdwReserved,
871 lpReserved,
872 dwFlags);
874 if ((lpszReserved != NULL) ||
875 (lpdwReserved != NULL) ||
876 (lpReserved != NULL))
878 ERR("Reserved value was not 0\n");
879 SetLastError(ERROR_INVALID_PARAMETER);
880 return FALSE;
882 if (dwFlags != 0)
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())
902 return FALSE;
904 if (!(pHeader = URLCache_LockIndex()))
905 return FALSE;
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);
914 return FALSE;
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);
927 return FALSE;
929 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
931 URLCache_UnlockIndex(pHeader);
933 return TRUE;
936 /***********************************************************************
937 * RetrieveUrlCacheEntryFileA (WININET.@)
940 BOOL WINAPI RetrieveUrlCacheEntryFileA(
941 IN LPCSTR lpszUrlName,
942 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
943 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
944 IN DWORD dwReserved
947 LPURLCACHE_HEADER pHeader;
948 CACHEFILE_ENTRY * pEntry;
949 URL_CACHEFILE_ENTRY * pUrlEntry;
951 if (!URLCache_OpenIndex())
952 return FALSE;
954 if (!(pHeader = URLCache_LockIndex()))
955 return FALSE;
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);
964 return FALSE;
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);
981 return FALSE;
983 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
985 URLCache_UnlockIndex(pHeader);
987 return TRUE;
990 /***********************************************************************
991 * UnlockUrlCacheEntryFileA (WININET.@)
994 BOOL WINAPI UnlockUrlCacheEntryFileA(
995 IN LPCSTR lpszUrlName,
996 IN DWORD dwReserved
999 LPURLCACHE_HEADER pHeader;
1000 CACHEFILE_ENTRY * pEntry;
1001 URL_CACHEFILE_ENTRY * pUrlEntry;
1003 if (dwReserved)
1005 ERR("dwReserved != 0\n");
1006 SetLastError(ERROR_INVALID_PARAMETER);
1007 return FALSE;
1010 if (!URLCache_OpenIndex())
1011 return FALSE;
1013 if (!(pHeader = URLCache_LockIndex()))
1014 return FALSE;
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);
1023 return FALSE;
1027 /* FIXME: check signature */
1029 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1031 if (pUrlEntry->dwUseCount == 0)
1033 URLCache_UnlockIndex(pHeader);
1034 return FALSE;
1036 pUrlEntry->dwUseCount--;
1037 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1039 URLCache_UnlockIndex(pHeader);
1041 return TRUE;
1044 /***********************************************************************
1045 * CreateUrlCacheEntryA (WININET.@)
1048 BOOL WINAPI CreateUrlCacheEntryA(
1049 IN LPCSTR lpszUrlName,
1050 IN DWORD dwExpectedFileSize,
1051 IN LPCSTR lpszFileExtension,
1052 OUT LPSTR lpszFileName,
1053 IN DWORD dwReserved
1056 LPURLCACHE_HEADER pHeader;
1057 CHAR szFile[MAX_PATH];
1058 CHAR szExtension[MAX_PATH];
1059 LPCSTR lpszUrlPart;
1060 LPCSTR lpszUrlEnd;
1061 LPCSTR lpszFileNameExtension;
1062 LPSTR lpszFileNameNoPath;
1063 int i;
1064 int countnoextension;
1065 BYTE CacheDir;
1066 LONG lBufferSize = MAX_PATH * sizeof(CHAR);
1067 BOOL bFound = FALSE;
1068 int count;
1070 if (dwReserved)
1072 ERR("dwReserved != 0\n");
1073 SetLastError(ERROR_INVALID_PARAMETER);
1074 return FALSE;
1077 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1080 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1081 lpszUrlEnd--;
1083 for (lpszUrlPart = lpszUrlEnd;
1084 (lpszUrlPart >= lpszUrlName);
1085 lpszUrlPart--)
1087 if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1089 bFound = TRUE;
1090 lpszUrlPart++;
1091 break;
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 : */
1107 else
1109 FIXME("need to generate a random filename");
1112 TRACE("File name: %s\n", szFile);
1114 if (!URLCache_OpenIndex())
1115 return FALSE;
1117 if (!(pHeader = URLCache_LockIndex()))
1118 return FALSE;
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++)
1142 HANDLE hFile;
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)
1149 CloseHandle(hFile);
1150 return TRUE;
1154 return FALSE;
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);
1177 BYTE cDirectory;
1178 BOOL bFound = FALSE;
1179 DWORD dwOffsetLocalFileName;
1180 DWORD dwOffsetHeader;
1181 DWORD dwFileSizeLow;
1182 DWORD dwFileSizeHigh;
1183 HANDLE hFile;
1185 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1186 debugstr_a(lpszUrlName),
1187 debugstr_a(lpszLocalFileName),
1188 CacheEntryType,
1189 lpHeaderInfo,
1190 dwHeaderSize,
1191 lpszFileExtension,
1192 dwReserved);
1194 if (dwReserved)
1196 ERR("dwReserved != 0\n");
1197 SetLastError(ERROR_INVALID_PARAMETER);
1198 return FALSE;
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());
1209 return FALSE;
1212 /* Get file size */
1213 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1214 if ((dwFileSizeLow == -1) && (GetLastError() != NO_ERROR))
1216 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1217 CloseHandle(hFile);
1218 return FALSE;
1221 CloseHandle(hFile);
1223 if (!URLCache_OpenIndex())
1224 return FALSE;
1226 if (!(pHeader = URLCache_LockIndex()))
1227 return FALSE;
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);
1234 return FALSE;
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);
1242 return FALSE;
1245 lpszLocalFileName += strlen(szCacheContentPath);
1247 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1249 if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1251 bFound = TRUE;
1252 break;
1256 if (!bFound)
1258 URLCache_UnlockIndex(pHeader);
1259 ERR("cache directory not found in path %s\n", lpszLocalFileName);
1260 SetLastError(ERROR_INVALID_PARAMETER);
1261 return FALSE;
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");
1281 return FALSE;
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;
1306 /*** Unknowns ***/
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);
1323 return FALSE;
1326 URLCache_UnlockIndex(pHeader);
1328 return TRUE;
1331 BOOL WINAPI ReadUrlCacheEntryStream(
1332 IN HANDLE hUrlCacheStream,
1333 IN DWORD dwLocation,
1334 IN OUT LPVOID lpBuffer,
1335 IN OUT LPDWORD lpdwLen,
1336 IN DWORD dwReserved
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);
1346 return FALSE;
1349 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1351 SetLastError(ERROR_INVALID_HANDLE);
1352 return FALSE;
1355 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == -1)
1356 return FALSE;
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,
1369 IN DWORD dwReserved
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;
1379 HANDLE hFile;
1381 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
1382 lpCacheEntryInfo,
1383 lpdwCacheEntryInfoBufferSize,
1384 dwReserved))
1386 return NULL;
1389 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1390 GENERIC_READ,
1391 FILE_SHARE_READ,
1392 NULL,
1393 OPEN_EXISTING,
1394 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
1395 NULL);
1396 if (hFile == INVALID_HANDLE_VALUE)
1397 return FALSE;
1399 /* allocate handle storage space */
1400 pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
1401 if (!pStream)
1403 CloseHandle(hFile);
1404 SetLastError(ERROR_OUTOFMEMORY);
1405 return FALSE;
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,
1419 IN DWORD dwReserved
1422 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1424 if (dwReserved != 0)
1426 ERR("dwReserved != 0\n");
1427 SetLastError(ERROR_INVALID_PARAMETER);
1428 return FALSE;
1431 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1433 SetLastError(ERROR_INVALID_HANDLE);
1434 return FALSE;
1437 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
1438 return FALSE;
1440 /* close file handle */
1441 CloseHandle(pStream->hFile);
1443 /* free allocated space */
1444 HeapFree(GetProcessHeap(), 0, pStream);
1446 return TRUE;
1450 /***********************************************************************
1451 * DeleteUrlCacheEntryA (WININET.@)
1454 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
1456 LPURLCACHE_HEADER pHeader;
1457 CACHEFILE_ENTRY * pEntry;
1458 DWORD dwStartBlock;
1459 DWORD dwBlock;
1460 BYTE * AllocationTable;
1462 if (!URLCache_OpenIndex())
1463 return FALSE;
1465 if (!(pHeader = URLCache_LockIndex()))
1466 return FALSE;
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);
1475 return FALSE;
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);
1492 return TRUE;
1495 /***********************************************************************
1496 * CreateUrlCacheGroup (WININET.@)
1499 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID
1500 lpReserved)
1502 FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
1503 return FALSE;
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);
1514 return 0;
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);
1525 return 0;
1528 /***********************************************************************
1529 * DeleteUrlCacheGroup (WININET.@)
1532 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
1534 FIXME("STUB\n");
1535 return FALSE;
1538 /***********************************************************************
1539 * SetUrlCacheEntryGroup (WININET.@)
1542 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
1543 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
1544 LPVOID lpReserved)
1546 FIXME("STUB\n");
1547 SetLastError(ERROR_FILE_NOT_FOUND);
1548 return FALSE;
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);
1561 return FALSE;
1564 /***********************************************************************
1565 * GetUrlCacheEntryInfoExW (WININET.@)
1568 BOOL WINAPI GetUrlCacheEntryInfoExW(
1569 LPCWSTR lpszUrl,
1570 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1571 LPDWORD lpdwCacheEntryInfoBufSize,
1572 LPWSTR lpszReserved,
1573 LPDWORD lpdwReserved,
1574 LPVOID lpReserved,
1575 DWORD dwFlags)
1577 FIXME(" url=%s, flags=%ld\n",debugstr_w(lpszUrl),dwFlags);
1578 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
1579 return FALSE;
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);
1591 return FALSE;
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);
1601 return FALSE;
1605 /***********************************************************************
1606 * SetUrlCacheEntryInfoA (WININET.@)
1608 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, DWORD dwFieldControl)
1610 FIXME("stub\n");
1611 return FALSE;
1614 /***********************************************************************
1615 * SetUrlCacheEntryInfoW (WININET.@)
1617 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1619 FIXME("stub\n");
1620 return FALSE;