Fixed header dependencies to be fully compatible with the Windows
[wine/multimedia.git] / dlls / wininet / urlcache.c
blob597147bf24ce9e55b9c611103ea1d23b9db1a9cb
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 <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "wininet.h"
38 #include "winerror.h"
39 #include "internet.h"
40 #include "winreg.h"
41 #include "shlwapi.h"
42 #include "wingdi.h"
43 #include "shlobj.h"
45 #include "wine/debug.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
49 #define ENTRY_START_OFFSET 0x4000
50 #define DIR_LENGTH 8
51 #define BLOCKSIZE 128
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
69 /* union
70 {*/
71 DWORD dwSignature; /* e.g. "URL " */
72 /* CHAR szSignature[4];
73 };*/
74 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
75 } CACHEFILE_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;
114 struct _HASH_ENTRY
116 DWORD dwHashKey;
117 DWORD dwOffsetEntry;
120 typedef struct _HASH_CACHEFILE_ENTRY
122 CACHEFILE_ENTRY CacheFileEntry;
123 DWORD dwAddressNext;
124 DWORD dwHashTableNumber;
125 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
126 } HASH_CACHEFILE_ENTRY;
128 typedef struct _DIRECTORY_DATA
130 DWORD dwUnknown;
131 char filename[DIR_LENGTH];
132 } DIRECTORY_DATA;
134 typedef struct _URLCACHE_HEADER
136 char szSignature[28];
137 DWORD dwFileSize;
138 DWORD dwOffsetFirstHashTable;
139 DWORD dwIndexCapacityInBlocks;
140 DWORD dwBlocksInUse; /* is this right? */
141 DWORD dwUnknown1;
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? */
147 DWORD dwUnknown7;
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
157 HANDLE hFile;
158 CHAR lpszUrl[1];
159 } 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 '!')
173 * RETURNS
174 * nothing
177 static void URLCache_PathToObjectName(LPSTR lpszPath, char replace)
179 char ch;
180 for (ch = *lpszPath; (ch = *lpszPath); lpszPath++)
182 if (ch == '\\')
183 *lpszPath = replace;
187 #ifndef CHAR_BIT
188 #define CHAR_BIT (8 * sizeof(CHAR))
189 #endif
191 /***********************************************************************
192 * URLCache_Allocation_BlockIsFree (Internal)
194 * Is the specified block number free?
196 * RETURNS
197 * zero if free
198 * non-zero otherwise
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
212 * RETURNS
213 * nothing
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
227 * RETURNS
228 * nothing
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
242 * RETURNS
243 * TRUE if it found the specified entry
244 * FALSE otherwise
247 static BOOL URLCache_FindEntry(LPCURLCACHE_HEADER pHeader, LPCSTR szUrl, CACHEFILE_ENTRY ** ppEntry)
249 CACHEFILE_ENTRY * pCurrentEntry;
250 DWORD dwBlockNumber;
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)
262 return FALSE;
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? */
278 return TRUE;
281 break;
282 case HASH_SIGNATURE: /* HASH entries parsed in FindEntryInHash */
283 case 0xDEADBEEF: /* this is always at offset 0x4000 in URL cache for some reason */
284 break;
285 default:
286 FIXME("Unknown entry %.4s ignored\n", (LPCSTR)&pCurrentEntry->dwSignature);
290 return FALSE;
293 /***********************************************************************
294 * URLCache_OpenIndex (Internal)
296 * Opens the index file and saves mapping handle in hCacheIndexMapping
298 * RETURNS
299 * TRUE if succeeded
300 * FALSE if failed
303 static BOOL URLCache_OpenIndex()
305 HANDLE hFile;
306 CHAR szFullPath[MAX_PATH];
307 CHAR szFileMappingName[MAX_PATH+10];
308 CHAR szMutexName[MAX_PATH+1];
309 DWORD dwFileSize;
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)))
320 return FALSE;
321 strcat(szCacheContentPath, CONTENT_DIRECTORY);
324 strcpy(szFullPath, szCacheContentPath);
325 strcat(szFullPath, "index.dat");
327 if (hCacheIndexMapping)
328 return TRUE;
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");
334 return FALSE;
337 dwFileSize = GetFileSize(hFile, NULL);
338 if (dwFileSize == INVALID_FILE_SIZE)
339 return FALSE;
341 if (dwFileSize == 0)
343 FIXME("need to create cache index file\n");
344 return FALSE;
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);
353 CloseHandle(hFile);
354 if (!hCacheIndexMapping)
356 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
357 return FALSE;
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);
369 return FALSE;
372 return TRUE;
375 /***********************************************************************
376 * URLCache_CloseIndex (Internal)
378 * Closes the index
380 * RETURNS
381 * nothing
384 #if 0 /* not used at the moment */
385 static BOOL URLCache_CloseIndex()
387 return CloseHandle(hCacheIndexMapping);
389 #endif
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.
397 * RETURNS
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;
405 DWORD dwBlockNumber;
406 DWORD dwFreeCounter;
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);
413 dwFreeCounter++)
414 TRACE("Found free block at no. %ld (0x%lx)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
416 if (dwFreeCounter == dwBlocksNeeded)
418 DWORD index;
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;
424 return TRUE;
427 return FALSE;
430 /***********************************************************************
431 * URLCache_DeleteEntry (Internal)
433 * Deletes the specified entry and frees the space allocated to it
435 * RETURNS
436 * TRUE if it succeeded
437 * FALSE if it failed
440 static BOOL URLCache_DeleteEntry(CACHEFILE_ENTRY * pEntry)
442 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
443 return TRUE;
446 /***********************************************************************
447 * URLCache_LockIndex (Internal)
450 static LPURLCACHE_HEADER URLCache_LockIndex()
452 BYTE index;
453 LPVOID pIndexData = MapViewOfFile(hCacheIndexMapping, FILE_MAP_WRITE, 0, 0, 0);
454 URLCACHE_HEADER * pHeader = (URLCACHE_HEADER *)pIndexData;
455 if (!pIndexData)
456 return FALSE;
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);
465 /* acquire mutex */
466 WaitForSingleObject(hMutex, INFINITE);
468 return pHeader;
471 /***********************************************************************
472 * URLCache_UnlockIndex (Internal)
475 static BOOL URLCache_UnlockIndex(LPURLCACHE_HEADER pHeader)
477 /* release mutex */
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.
489 * RETURNS
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)
496 LONG nRequired;
497 if (Directory >= pHeader->DirectoryCount)
499 *lpBufferSize = 0;
500 return FALSE;
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;
511 return TRUE;
513 *lpBufferSize = nRequired;
514 return FALSE;
517 /***********************************************************************
518 * URLCache_CopyEntry (Internal)
520 * Copies an entry from the cache index file to the Win32 structure
522 * RETURNS
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);
598 return FALSE;
600 *lpdwBufferSize = dwRequiredSize;
601 return TRUE;
604 /***********************************************************************
605 * URLCache_HashKey (Internal)
607 * Returns the hash key for a given string
609 * RETURNS
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
653 BYTE key[4];
654 int i;
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
685 * RETURNS
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
700 * note:
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))
715 int i;
716 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
718 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
719 continue;
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);
725 continue;
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);
734 return TRUE;
738 return FALSE;
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)
747 * RETURNS
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))
773 int i;
774 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
776 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
777 continue;
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);
783 continue;
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;
792 return TRUE;
796 return FALSE;
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.
806 * RETURNS
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))
826 int i;
827 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
829 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
830 break;
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);
836 break;
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;
846 return TRUE;
850 FIXME("need to create another hash table\n");
851 return FALSE;
854 /***********************************************************************
855 * GetUrlCacheEntryInfoExA (WININET.@)
858 BOOL WINAPI GetUrlCacheEntryInfoExA(
859 LPCSTR lpszUrl,
860 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
861 LPDWORD lpdwCacheEntryInfoBufSize,
862 LPSTR lpszReserved,
863 LPDWORD lpdwReserved,
864 LPVOID lpReserved,
865 DWORD dwFlags)
867 TRACE("(%s, %p, %p, %p, %p, %p, %lx)\n",
868 debugstr_a(lpszUrl),
869 lpCacheEntryInfo,
870 lpdwCacheEntryInfoBufSize,
871 lpszReserved,
872 lpdwReserved,
873 lpReserved,
874 dwFlags);
876 if ((lpszReserved != NULL) ||
877 (lpdwReserved != NULL) ||
878 (lpReserved != NULL))
880 ERR("Reserved value was not 0\n");
881 SetLastError(ERROR_INVALID_PARAMETER);
882 return FALSE;
884 if (dwFlags != 0)
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())
904 return FALSE;
906 if (!(pHeader = URLCache_LockIndex()))
907 return FALSE;
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);
916 return FALSE;
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);
929 return FALSE;
931 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
933 URLCache_UnlockIndex(pHeader);
935 return TRUE;
938 /***********************************************************************
939 * RetrieveUrlCacheEntryFileA (WININET.@)
942 BOOL WINAPI RetrieveUrlCacheEntryFileA(
943 IN LPCSTR lpszUrlName,
944 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
945 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
946 IN DWORD dwReserved
949 LPURLCACHE_HEADER pHeader;
950 CACHEFILE_ENTRY * pEntry;
951 URL_CACHEFILE_ENTRY * pUrlEntry;
953 if (!URLCache_OpenIndex())
954 return FALSE;
956 if (!(pHeader = URLCache_LockIndex()))
957 return FALSE;
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);
966 return FALSE;
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);
983 return FALSE;
985 TRACE("Local File Name: %s\n", lpCacheEntryInfo->lpszLocalFileName);
987 URLCache_UnlockIndex(pHeader);
989 return TRUE;
992 /***********************************************************************
993 * UnlockUrlCacheEntryFileA (WININET.@)
996 BOOL WINAPI UnlockUrlCacheEntryFileA(
997 IN LPCSTR lpszUrlName,
998 IN DWORD dwReserved
1001 LPURLCACHE_HEADER pHeader;
1002 CACHEFILE_ENTRY * pEntry;
1003 URL_CACHEFILE_ENTRY * pUrlEntry;
1005 if (dwReserved)
1007 ERR("dwReserved != 0\n");
1008 SetLastError(ERROR_INVALID_PARAMETER);
1009 return FALSE;
1012 if (!URLCache_OpenIndex())
1013 return FALSE;
1015 if (!(pHeader = URLCache_LockIndex()))
1016 return FALSE;
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);
1025 return FALSE;
1029 /* FIXME: check signature */
1031 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1033 if (pUrlEntry->dwUseCount == 0)
1035 URLCache_UnlockIndex(pHeader);
1036 return FALSE;
1038 pUrlEntry->dwUseCount--;
1039 URLCache_HashEntrySetUse(pHeader, lpszUrlName, pUrlEntry->dwUseCount);
1041 URLCache_UnlockIndex(pHeader);
1043 return TRUE;
1046 /***********************************************************************
1047 * CreateUrlCacheEntryA (WININET.@)
1050 BOOL WINAPI CreateUrlCacheEntryA(
1051 IN LPCSTR lpszUrlName,
1052 IN DWORD dwExpectedFileSize,
1053 IN LPCSTR lpszFileExtension,
1054 OUT LPSTR lpszFileName,
1055 IN DWORD dwReserved
1058 LPURLCACHE_HEADER pHeader;
1059 CHAR szFile[MAX_PATH];
1060 CHAR szExtension[MAX_PATH];
1061 LPCSTR lpszUrlPart;
1062 LPCSTR lpszUrlEnd;
1063 LPCSTR lpszFileNameExtension;
1064 LPSTR lpszFileNameNoPath;
1065 int i;
1066 int countnoextension;
1067 BYTE CacheDir;
1068 LONG lBufferSize = MAX_PATH * sizeof(CHAR);
1069 BOOL bFound = FALSE;
1070 int count;
1072 if (dwReserved)
1074 ERR("dwReserved != 0\n");
1075 SetLastError(ERROR_INVALID_PARAMETER);
1076 return FALSE;
1079 for (lpszUrlEnd = lpszUrlName; *lpszUrlEnd; lpszUrlEnd++)
1082 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/'))
1083 lpszUrlEnd--;
1085 for (lpszUrlPart = lpszUrlEnd;
1086 (lpszUrlPart >= lpszUrlName);
1087 lpszUrlPart--)
1089 if ((*lpszUrlPart == '/') && ((lpszUrlEnd - lpszUrlPart) > 1))
1091 bFound = TRUE;
1092 lpszUrlPart++;
1093 break;
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 : */
1109 else
1111 FIXME("need to generate a random filename");
1114 TRACE("File name: %s\n", szFile);
1116 if (!URLCache_OpenIndex())
1117 return FALSE;
1119 if (!(pHeader = URLCache_LockIndex()))
1120 return FALSE;
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++)
1144 HANDLE hFile;
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)
1151 CloseHandle(hFile);
1152 return TRUE;
1156 return FALSE;
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);
1179 BYTE cDirectory;
1180 BOOL bFound = FALSE;
1181 DWORD dwOffsetLocalFileName;
1182 DWORD dwOffsetHeader;
1183 DWORD dwFileSizeLow;
1184 DWORD dwFileSizeHigh;
1185 HANDLE hFile;
1187 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %p)\n",
1188 debugstr_a(lpszUrlName),
1189 debugstr_a(lpszLocalFileName),
1190 CacheEntryType,
1191 lpHeaderInfo,
1192 dwHeaderSize,
1193 lpszFileExtension,
1194 dwReserved);
1196 if (dwReserved)
1198 ERR("dwReserved != 0\n");
1199 SetLastError(ERROR_INVALID_PARAMETER);
1200 return FALSE;
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());
1211 return FALSE;
1214 /* Get file size */
1215 dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
1216 if ((dwFileSizeLow == -1) && (GetLastError() != NO_ERROR))
1218 ERR("couldn't get file size (error is %ld)\n", GetLastError());
1219 CloseHandle(hFile);
1220 return FALSE;
1223 CloseHandle(hFile);
1225 if (!URLCache_OpenIndex())
1226 return FALSE;
1228 if (!(pHeader = URLCache_LockIndex()))
1229 return FALSE;
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);
1236 return FALSE;
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);
1244 return FALSE;
1247 lpszLocalFileName += strlen(szCacheContentPath);
1249 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
1251 if (!strncmp(pHeader->directory_data[cDirectory].filename, lpszLocalFileName, DIR_LENGTH))
1253 bFound = TRUE;
1254 break;
1258 if (!bFound)
1260 URLCache_UnlockIndex(pHeader);
1261 ERR("cache directory not found in path %s\n", lpszLocalFileName);
1262 SetLastError(ERROR_INVALID_PARAMETER);
1263 return FALSE;
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");
1283 return FALSE;
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;
1308 /*** Unknowns ***/
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);
1325 return FALSE;
1328 URLCache_UnlockIndex(pHeader);
1330 return TRUE;
1333 BOOL WINAPI ReadUrlCacheEntryStream(
1334 IN HANDLE hUrlCacheStream,
1335 IN DWORD dwLocation,
1336 IN OUT LPVOID lpBuffer,
1337 IN OUT LPDWORD lpdwLen,
1338 IN DWORD dwReserved
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);
1348 return FALSE;
1351 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1353 SetLastError(ERROR_INVALID_HANDLE);
1354 return FALSE;
1357 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == -1)
1358 return FALSE;
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,
1371 IN DWORD dwReserved
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;
1381 HANDLE hFile;
1383 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
1384 lpCacheEntryInfo,
1385 lpdwCacheEntryInfoBufferSize,
1386 dwReserved))
1388 return NULL;
1391 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
1392 GENERIC_READ,
1393 FILE_SHARE_READ,
1394 NULL,
1395 OPEN_EXISTING,
1396 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
1397 NULL);
1398 if (hFile == INVALID_HANDLE_VALUE)
1399 return FALSE;
1401 /* allocate handle storage space */
1402 pStream = (STREAM_HANDLE *)HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
1403 if (!pStream)
1405 CloseHandle(hFile);
1406 SetLastError(ERROR_OUTOFMEMORY);
1407 return FALSE;
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,
1421 IN DWORD dwReserved
1424 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
1426 if (dwReserved != 0)
1428 ERR("dwReserved != 0\n");
1429 SetLastError(ERROR_INVALID_PARAMETER);
1430 return FALSE;
1433 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
1435 SetLastError(ERROR_INVALID_HANDLE);
1436 return FALSE;
1439 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
1440 return FALSE;
1442 /* close file handle */
1443 CloseHandle(pStream->hFile);
1445 /* free allocated space */
1446 HeapFree(GetProcessHeap(), 0, pStream);
1448 return TRUE;
1452 /***********************************************************************
1453 * DeleteUrlCacheEntryA (WININET.@)
1456 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
1458 LPURLCACHE_HEADER pHeader;
1459 CACHEFILE_ENTRY * pEntry;
1460 DWORD dwStartBlock;
1461 DWORD dwBlock;
1462 BYTE * AllocationTable;
1464 if (!URLCache_OpenIndex())
1465 return FALSE;
1467 if (!(pHeader = URLCache_LockIndex()))
1468 return FALSE;
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);
1477 return FALSE;
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);
1494 return TRUE;
1497 /***********************************************************************
1498 * CreateUrlCacheGroup (WININET.@)
1501 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID
1502 lpReserved)
1504 FIXME("(%lx, %p): stub\n", dwFlags, lpReserved);
1505 return FALSE;
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);
1516 return 0;
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);
1527 return 0;
1530 /***********************************************************************
1531 * DeleteUrlCacheGroup (WININET.@)
1534 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
1536 FIXME("STUB\n");
1537 return FALSE;
1540 /***********************************************************************
1541 * SetUrlCacheEntryGroup (WININET.@)
1544 BOOL WINAPI SetUrlCacheEntryGroup(LPCSTR lpszUrlName, DWORD dwFlags,
1545 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
1546 LPVOID lpReserved)
1548 FIXME("STUB\n");
1549 SetLastError(ERROR_FILE_NOT_FOUND);
1550 return FALSE;
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);
1563 return FALSE;
1566 /***********************************************************************
1567 * GetUrlCacheEntryInfoExW (WININET.@)
1570 BOOL WINAPI GetUrlCacheEntryInfoExW(
1571 LPCWSTR lpszUrl,
1572 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1573 LPDWORD lpdwCacheEntryInfoBufSize,
1574 LPWSTR lpszReserved,
1575 LPDWORD lpdwReserved,
1576 LPVOID lpReserved,
1577 DWORD dwFlags)
1579 FIXME(" url=%s, flags=%ld\n",debugstr_w(lpszUrl),dwFlags);
1580 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
1581 return FALSE;
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);
1593 return FALSE;
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);
1603 return FALSE;
1607 /***********************************************************************
1608 * SetUrlCacheEntryInfoA (WININET.@)
1610 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, DWORD dwFieldControl)
1612 FIXME("stub\n");
1613 return FALSE;
1616 /***********************************************************************
1617 * SetUrlCacheEntryInfoW (WININET.@)
1619 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrlName, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1621 FIXME("stub\n");
1622 return FALSE;