wininet: Added basic cookie domain validation in set_cookie function.
[wine] / dlls / wininet / tests / urlcache.c
1 /*
2  * URL Cache Tests
3  *
4  * Copyright 2008 Robert Shearman for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wininet.h"
28 #include "winineti.h"
29
30 #include "wine/test.h"
31
32 static const char test_url[] = "http://urlcachetest.winehq.org/index.html";
33 static const WCHAR test_urlW[] = {'h','t','t','p',':','/','/','u','r','l','c','a','c','h','e','t','e','s','t','.',
34     'w','i','n','e','h','q','.','o','r','g','/','i','n','d','e','x','.','h','t','m','l',0};
35 static const char test_url1[] = "Visited: user@http://urlcachetest.winehq.org/index.html";
36 static const char test_hash_collisions1[] = "Visited: http://winehq.org/doc0.html";
37 static const char test_hash_collisions2[] = "Visited: http://winehq.org/doc75651909.html";
38
39 static BOOL (WINAPI *pDeleteUrlCacheEntryA)(LPCSTR);
40 static BOOL (WINAPI *pUnlockUrlCacheEntryFileA)(LPCSTR,DWORD);
41
42 static char filenameA[MAX_PATH + 1];
43 static char filenameA1[MAX_PATH + 1];
44
45 static void check_cache_entry_infoA(const char *returnedfrom, LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo)
46 {
47     ok(lpCacheEntryInfo->dwStructSize == sizeof(*lpCacheEntryInfo), "%s: dwStructSize was %d\n", returnedfrom, lpCacheEntryInfo->dwStructSize);
48     ok(!strcmp(lpCacheEntryInfo->lpszSourceUrlName, test_url), "%s: lpszSourceUrlName should be %s instead of %s\n", returnedfrom, test_url, lpCacheEntryInfo->lpszSourceUrlName);
49     ok(!strcmp(lpCacheEntryInfo->lpszLocalFileName, filenameA), "%s: lpszLocalFileName should be %s instead of %s\n", returnedfrom, filenameA, lpCacheEntryInfo->lpszLocalFileName);
50     ok(!strcmp(lpCacheEntryInfo->lpszFileExtension, "html"), "%s: lpszFileExtension should be html instead of %s\n", returnedfrom, lpCacheEntryInfo->lpszFileExtension);
51 }
52
53 static void test_find_url_cache_entriesA(void)
54 {
55     BOOL ret;
56     HANDLE hEnumHandle;
57     BOOL found = FALSE;
58     DWORD cbCacheEntryInfo;
59     DWORD cbCacheEntryInfoSaved;
60     LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo;
61
62     cbCacheEntryInfo = 0;
63     SetLastError(0xdeadbeef);
64     hEnumHandle = FindFirstUrlCacheEntry(NULL, NULL, &cbCacheEntryInfo);
65     ok(!hEnumHandle, "FindFirstUrlCacheEntry should have failed\n");
66     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "FindFirstUrlCacheEntry should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError());
67     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo * sizeof(char));
68     cbCacheEntryInfoSaved = cbCacheEntryInfo;
69     hEnumHandle = FindFirstUrlCacheEntry(NULL, lpCacheEntryInfo, &cbCacheEntryInfo);
70     ok(hEnumHandle != NULL, "FindFirstUrlCacheEntry failed with error %d\n", GetLastError());
71     while (TRUE)
72     {
73         if (!strcmp(lpCacheEntryInfo->lpszSourceUrlName, test_url))
74         {
75             found = TRUE;
76             ret = TRUE;
77             break;
78         }
79         SetLastError(0xdeadbeef);
80         cbCacheEntryInfo = cbCacheEntryInfoSaved;
81         ret = FindNextUrlCacheEntry(hEnumHandle, lpCacheEntryInfo, &cbCacheEntryInfo);
82         if (!ret)
83         {
84             if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
85             {
86                 lpCacheEntryInfo = HeapReAlloc(GetProcessHeap(), 0, lpCacheEntryInfo, cbCacheEntryInfo);
87                 cbCacheEntryInfoSaved = cbCacheEntryInfo;
88                 ret = FindNextUrlCacheEntry(hEnumHandle, lpCacheEntryInfo, &cbCacheEntryInfo);
89             }
90         }
91         if (!ret)
92             break;
93     }
94     ok(ret, "FindNextUrlCacheEntry failed with error %d\n", GetLastError());
95     ok(found, "Committed url cache entry not found during enumeration\n");
96
97     ret = FindCloseUrlCache(hEnumHandle);
98     ok(ret, "FindCloseUrlCache failed with error %d\n", GetLastError());
99 }
100
101 static void test_GetUrlCacheEntryInfoExA(void)
102 {
103     BOOL ret;
104     DWORD cbCacheEntryInfo, cbRedirectUrl;
105     LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo;
106
107     SetLastError(0xdeadbeef);
108     ret = GetUrlCacheEntryInfoEx(NULL, NULL, NULL, NULL, NULL, NULL, 0);
109     ok(!ret, "GetUrlCacheEntryInfoEx with NULL URL and NULL args should have failed\n");
110     ok(GetLastError() == ERROR_INVALID_PARAMETER,
111        "GetUrlCacheEntryInfoEx with NULL URL and NULL args should have set last error to ERROR_INVALID_PARAMETER instead of %d\n", GetLastError());
112
113     cbCacheEntryInfo = sizeof(INTERNET_CACHE_ENTRY_INFO);
114     SetLastError(0xdeadbeef);
115     ret = GetUrlCacheEntryInfoEx("", NULL, &cbCacheEntryInfo, NULL, NULL, NULL, 0);
116     ok(!ret, "GetUrlCacheEntryInfoEx with zero-length buffer should fail\n");
117     ok(GetLastError() == ERROR_FILE_NOT_FOUND,
118        "GetUrlCacheEntryInfoEx should have set last error to ERROR_FILE_NOT_FOUND instead of %d\n", GetLastError());
119
120     ret = GetUrlCacheEntryInfoEx(test_url, NULL, NULL, NULL, NULL, NULL, 0);
121     ok(ret, "GetUrlCacheEntryInfoEx with NULL args failed with error %d\n", GetLastError());
122
123     cbCacheEntryInfo = 0;
124     SetLastError(0xdeadbeef);
125     ret = GetUrlCacheEntryInfoEx(test_url, NULL, &cbCacheEntryInfo, NULL, NULL, NULL, 0);
126     ok(!ret, "GetUrlCacheEntryInfoEx with zero-length buffer should fail\n");
127     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
128        "GetUrlCacheEntryInfoEx should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError());
129
130     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
131
132     SetLastError(0xdeadbeef);
133     ret = GetUrlCacheEntryInfoEx(test_url, NULL, NULL, NULL, NULL, NULL, 0x200 /*GET_INSTALLED_ENTRY*/);
134     ok(!ret, "GetUrlCacheEntryInfoEx succeeded\n");
135     ok(GetLastError() == ERROR_FILE_NOT_FOUND,
136        "GetUrlCacheEntryInfoEx should have set last error to ERROR_FILE_NOT_FOUND instead of %d\n", GetLastError());
137
138     /* Unicode version of function seems to ignore 0x200 flag */
139     ret = GetUrlCacheEntryInfoExW(test_urlW, NULL, NULL, NULL, NULL, NULL, 0x200 /*GET_INSTALLED_ENTRY*/);
140     ok(ret, "GetUrlCacheEntryInfoExW failed with error %d\n", GetLastError());
141
142     ret = GetUrlCacheEntryInfoEx(test_url, lpCacheEntryInfo, &cbCacheEntryInfo, NULL, NULL, NULL, 0);
143     ok(ret, "GetUrlCacheEntryInfoEx failed with error %d\n", GetLastError());
144
145     if (ret) check_cache_entry_infoA("GetUrlCacheEntryInfoEx", lpCacheEntryInfo);
146
147     lpCacheEntryInfo->CacheEntryType |= 0x10000000; /* INSTALLED_CACHE_ENTRY */
148     ret = SetUrlCacheEntryInfoA(test_url, lpCacheEntryInfo, CACHE_ENTRY_ATTRIBUTE_FC);
149     ok(ret, "SetUrlCacheEntryInfoA failed with error %d\n", GetLastError());
150
151     SetLastError(0xdeadbeef);
152     ret = GetUrlCacheEntryInfoEx(test_url, NULL, NULL, NULL, NULL, NULL, 0x200 /*GET_INSTALLED_ENTRY*/);
153     ok(ret, "GetUrlCacheEntryInfoEx failed with error %d\n", GetLastError());
154
155     cbCacheEntryInfo = 100000;
156     SetLastError(0xdeadbeef);
157     ret = GetUrlCacheEntryInfoEx(test_url, NULL, &cbCacheEntryInfo, NULL, NULL, NULL, 0);
158     ok(!ret, "GetUrlCacheEntryInfoEx with zero-length buffer should fail\n");
159     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetUrlCacheEntryInfoEx should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError());
160
161     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
162
163     /* Querying the redirect URL fails with ERROR_INVALID_PARAMETER */
164     SetLastError(0xdeadbeef);
165     ret = GetUrlCacheEntryInfoEx(test_url, NULL, NULL, NULL, &cbRedirectUrl, NULL, 0);
166     ok(!ret, "GetUrlCacheEntryInfoEx should have failed\n");
167     ok(GetLastError() == ERROR_INVALID_PARAMETER,
168        "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
169     SetLastError(0xdeadbeef);
170     ret = GetUrlCacheEntryInfoEx(test_url, NULL, &cbCacheEntryInfo, NULL, &cbRedirectUrl, NULL, 0);
171     ok(!ret, "GetUrlCacheEntryInfoEx should have failed\n");
172     ok(GetLastError() == ERROR_INVALID_PARAMETER,
173        "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
174 }
175
176 static void test_RetrieveUrlCacheEntryA(void)
177 {
178     BOOL ret;
179     DWORD cbCacheEntryInfo;
180
181     cbCacheEntryInfo = 0;
182     SetLastError(0xdeadbeef);
183     ret = RetrieveUrlCacheEntryFile(NULL, NULL, &cbCacheEntryInfo, 0);
184     ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n");
185     ok(GetLastError() == ERROR_INVALID_PARAMETER, "RetrieveUrlCacheEntryFile should have set last error to ERROR_INVALID_PARAMETER instead of %d\n", GetLastError());
186
187     if (0)
188     {
189         /* Crashes on Win9x, NT4 and W2K */
190         SetLastError(0xdeadbeef);
191         ret = RetrieveUrlCacheEntryFile(test_url, NULL, NULL, 0);
192         ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n");
193         ok(GetLastError() == ERROR_INVALID_PARAMETER, "RetrieveUrlCacheEntryFile should have set last error to ERROR_INVALID_PARAMETER instead of %d\n", GetLastError());
194     }
195
196     SetLastError(0xdeadbeef);
197     cbCacheEntryInfo = 100000;
198     ret = RetrieveUrlCacheEntryFile(NULL, NULL, &cbCacheEntryInfo, 0);
199     ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n");
200     ok(GetLastError() == ERROR_INVALID_PARAMETER, "RetrieveUrlCacheEntryFile should have set last error to ERROR_INVALID_PARAMETER instead of %d\n", GetLastError());
201 }
202
203 static void test_IsUrlCacheEntryExpiredA(void)
204 {
205     static const char uncached_url[] =
206         "What's the airspeed velocity of an unladen swallow?";
207     BOOL ret;
208     FILETIME ft;
209     DWORD size;
210     LPINTERNET_CACHE_ENTRY_INFO info;
211     ULARGE_INTEGER exp_time;
212
213     /* The function returns TRUE when the output time is NULL or the tested URL
214      * is NULL.
215      */
216     ret = IsUrlCacheEntryExpiredA(NULL, 0, NULL);
217     ok(ret, "expected TRUE\n");
218     ft.dwLowDateTime = 0xdeadbeef;
219     ft.dwHighDateTime = 0xbaadf00d;
220     ret = IsUrlCacheEntryExpiredA(NULL, 0, &ft);
221     ok(ret, "expected TRUE\n");
222     ok(ft.dwLowDateTime == 0xdeadbeef && ft.dwHighDateTime == 0xbaadf00d,
223        "expected time to be unchanged, got (%u,%u)\n",
224        ft.dwLowDateTime, ft.dwHighDateTime);
225     ret = IsUrlCacheEntryExpiredA(test_url, 0, NULL);
226     ok(ret, "expected TRUE\n");
227
228     /* The return value should indicate whether the URL is expired,
229      * and the filetime indicates the last modified time, but a cache entry
230      * with a zero expire time is "not expired".
231      */
232     ft.dwLowDateTime = 0xdeadbeef;
233     ft.dwHighDateTime = 0xbaadf00d;
234     ret = IsUrlCacheEntryExpiredA(test_url, 0, &ft);
235     ok(!ret, "expected FALSE\n");
236     ok(!ft.dwLowDateTime && !ft.dwHighDateTime,
237        "expected time (0,0), got (%u,%u)\n",
238        ft.dwLowDateTime, ft.dwHighDateTime);
239
240     /* Same behavior with bogus flags. */
241     ft.dwLowDateTime = 0xdeadbeef;
242     ft.dwHighDateTime = 0xbaadf00d;
243     ret = IsUrlCacheEntryExpiredA(test_url, 0xffffffff, &ft);
244     ok(!ret, "expected FALSE\n");
245     ok(!ft.dwLowDateTime && !ft.dwHighDateTime,
246        "expected time (0,0), got (%u,%u)\n",
247        ft.dwLowDateTime, ft.dwHighDateTime);
248
249     /* Set the expire time to a point in the past.. */
250     ret = GetUrlCacheEntryInfo(test_url, NULL, &size);
251     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
252     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
253        "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
254     info = HeapAlloc(GetProcessHeap(), 0, size);
255     ret = GetUrlCacheEntryInfo(test_url, info, &size);
256     ok(ret, "GetUrlCacheEntryInfo failed: %d\n", GetLastError());
257     GetSystemTimeAsFileTime(&info->ExpireTime);
258     exp_time.u.LowPart = info->ExpireTime.dwLowDateTime;
259     exp_time.u.HighPart = info->ExpireTime.dwHighDateTime;
260     exp_time.QuadPart -= 10 * 60 * (ULONGLONG)10000000;
261     info->ExpireTime.dwLowDateTime = exp_time.u.LowPart;
262     info->ExpireTime.dwHighDateTime = exp_time.u.HighPart;
263     ret = SetUrlCacheEntryInfo(test_url, info, CACHE_ENTRY_EXPTIME_FC);
264     ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError());
265     ft.dwLowDateTime = 0xdeadbeef;
266     ft.dwHighDateTime = 0xbaadf00d;
267     /* and the entry should be expired. */
268     ret = IsUrlCacheEntryExpiredA(test_url, 0, &ft);
269     ok(ret, "expected TRUE\n");
270     /* The modified time returned is 0. */
271     ok(!ft.dwLowDateTime && !ft.dwHighDateTime,
272        "expected time (0,0), got (%u,%u)\n",
273        ft.dwLowDateTime, ft.dwHighDateTime);
274     /* Set the expire time to a point in the future.. */
275     exp_time.QuadPart += 20 * 60 * (ULONGLONG)10000000;
276     info->ExpireTime.dwLowDateTime = exp_time.u.LowPart;
277     info->ExpireTime.dwHighDateTime = exp_time.u.HighPart;
278     ret = SetUrlCacheEntryInfo(test_url, info, CACHE_ENTRY_EXPTIME_FC);
279     ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError());
280     ft.dwLowDateTime = 0xdeadbeef;
281     ft.dwHighDateTime = 0xbaadf00d;
282     /* and the entry should no longer be expired. */
283     ret = IsUrlCacheEntryExpiredA(test_url, 0, &ft);
284     ok(!ret, "expected FALSE\n");
285     /* The modified time returned is still 0. */
286     ok(!ft.dwLowDateTime && !ft.dwHighDateTime,
287        "expected time (0,0), got (%u,%u)\n",
288        ft.dwLowDateTime, ft.dwHighDateTime);
289     /* Set the modified time... */
290     GetSystemTimeAsFileTime(&info->LastModifiedTime);
291     ret = SetUrlCacheEntryInfo(test_url, info, CACHE_ENTRY_MODTIME_FC);
292     ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError());
293     /* and the entry should still be unexpired.. */
294     ret = IsUrlCacheEntryExpiredA(test_url, 0, &ft);
295     ok(!ret, "expected FALSE\n");
296     /* but the modified time returned is the last modified time just set. */
297     ok(ft.dwLowDateTime == info->LastModifiedTime.dwLowDateTime &&
298        ft.dwHighDateTime == info->LastModifiedTime.dwHighDateTime,
299        "expected time (%u,%u), got (%u,%u)\n",
300        info->LastModifiedTime.dwLowDateTime,
301        info->LastModifiedTime.dwHighDateTime,
302        ft.dwLowDateTime, ft.dwHighDateTime);
303     HeapFree(GetProcessHeap(), 0, info);
304
305     /* An uncached URL is implicitly expired, but with unknown time. */
306     ft.dwLowDateTime = 0xdeadbeef;
307     ft.dwHighDateTime = 0xbaadf00d;
308     ret = IsUrlCacheEntryExpiredA(uncached_url, 0, &ft);
309     ok(ret, "expected TRUE\n");
310     ok(!ft.dwLowDateTime && !ft.dwHighDateTime,
311        "expected time (0,0), got (%u,%u)\n",
312        ft.dwLowDateTime, ft.dwHighDateTime);
313 }
314
315 static void _check_file_exists(LONG l, LPCSTR filename)
316 {
317     HANDLE file;
318
319     file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
320                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
321     ok_(__FILE__,l)(file != INVALID_HANDLE_VALUE,
322                     "expected file to exist, CreateFile failed with error %d\n",
323                     GetLastError());
324     CloseHandle(file);
325 }
326
327 #define check_file_exists(f) _check_file_exists(__LINE__, f)
328
329 static void _check_file_not_exists(LONG l, LPCSTR filename)
330 {
331     HANDLE file;
332
333     file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
334                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
335     ok_(__FILE__,l)(file == INVALID_HANDLE_VALUE,
336                     "expected file not to exist\n");
337     if (file != INVALID_HANDLE_VALUE)
338         CloseHandle(file);
339 }
340
341 #define check_file_not_exists(f) _check_file_not_exists(__LINE__, f)
342
343 static void create_and_write_file(LPCSTR filename, void *data, DWORD len)
344 {
345     HANDLE file;
346     DWORD written;
347     BOOL ret;
348
349     file = CreateFileA(filename, GENERIC_WRITE,
350                        FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
351                        FILE_ATTRIBUTE_NORMAL, NULL);
352     ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed with error %d\n", GetLastError());
353
354     ret = WriteFile(file, data, len, &written, NULL);
355     ok(ret, "WriteFile failed with error %d\n", GetLastError());
356
357     CloseHandle(file);
358 }
359
360 static void test_urlcacheA(void)
361 {
362     static char ok_header[] = "HTTP/1.0 200 OK\r\n\r\n";
363     BOOL ret;
364     HANDLE hFile;
365     BYTE zero_byte = 0;
366     LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo;
367     LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo2;
368     DWORD cbCacheEntryInfo;
369     static const FILETIME filetime_zero;
370     FILETIME now;
371
372     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA, 0);
373     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
374
375     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA1, 0);
376     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
377     check_file_exists(filenameA1);
378     DeleteFileA(filenameA1);
379
380     ok(lstrcmpiA(filenameA, filenameA1), "expected a different file name\n");
381
382     create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte));
383
384     ret = CommitUrlCacheEntry(test_url1, NULL, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL);
385     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
386     cbCacheEntryInfo = 0;
387     ret = GetUrlCacheEntryInfo(test_url1, NULL, &cbCacheEntryInfo);
388     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
389     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
390        "GetUrlCacheEntryInfo should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError());
391     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
392     ret = GetUrlCacheEntryInfo(test_url1, lpCacheEntryInfo, &cbCacheEntryInfo);
393     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
394     ok(!memcmp(&lpCacheEntryInfo->ExpireTime, &filetime_zero, sizeof(FILETIME)),
395        "expected zero ExpireTime\n");
396     ok(!memcmp(&lpCacheEntryInfo->LastModifiedTime, &filetime_zero, sizeof(FILETIME)),
397        "expected zero LastModifiedTime\n");
398     ok(lpCacheEntryInfo->CacheEntryType == (NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY) ||
399        broken(lpCacheEntryInfo->CacheEntryType == NORMAL_CACHE_ENTRY /* NT4/W2k */),
400        "expected type NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY, got %08x\n",
401        lpCacheEntryInfo->CacheEntryType);
402     ok(!U(*lpCacheEntryInfo).dwExemptDelta, "expected dwExemptDelta 0, got %d\n",
403        U(*lpCacheEntryInfo).dwExemptDelta);
404
405     /* Make sure there is a notable change in timestamps */
406     Sleep(1000);
407
408     /* A subsequent commit with a different time/type doesn't change most of the entry */
409     GetSystemTimeAsFileTime(&now);
410     ret = CommitUrlCacheEntry(test_url1, NULL, now, now, NORMAL_CACHE_ENTRY,
411             (LPBYTE)ok_header, strlen(ok_header), NULL, NULL);
412     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
413     cbCacheEntryInfo = 0;
414     ret = GetUrlCacheEntryInfo(test_url1, NULL, &cbCacheEntryInfo);
415     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
416     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
417        "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
418     lpCacheEntryInfo2 = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
419     ret = GetUrlCacheEntryInfo(test_url1, lpCacheEntryInfo2, &cbCacheEntryInfo);
420     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
421     /* but it does change the time.. */
422     ok(memcmp(&lpCacheEntryInfo2->ExpireTime, &filetime_zero, sizeof(FILETIME)),
423        "expected positive ExpireTime\n");
424     ok(memcmp(&lpCacheEntryInfo2->LastModifiedTime, &filetime_zero, sizeof(FILETIME)),
425        "expected positive LastModifiedTime\n");
426     ok(lpCacheEntryInfo2->CacheEntryType == (NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY) ||
427        broken(lpCacheEntryInfo2->CacheEntryType == NORMAL_CACHE_ENTRY /* NT4/W2k */),
428        "expected type NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY, got %08x\n",
429        lpCacheEntryInfo2->CacheEntryType);
430     /* and set the headers. */
431     ok(lpCacheEntryInfo2->dwHeaderInfoSize == 19,
432         "expected headers size 19, got %d\n",
433         lpCacheEntryInfo2->dwHeaderInfoSize);
434     /* Hit rate gets incremented by 1 */
435     ok((lpCacheEntryInfo->dwHitRate + 1) == lpCacheEntryInfo2->dwHitRate,
436         "HitRate not incremented by one on commit\n");
437     /* Last access time should be updated */
438     ok(!(lpCacheEntryInfo->LastAccessTime.dwHighDateTime == lpCacheEntryInfo2->LastAccessTime.dwHighDateTime &&
439         lpCacheEntryInfo->LastAccessTime.dwLowDateTime == lpCacheEntryInfo2->LastAccessTime.dwLowDateTime),
440         "Last accessed time was not updated by commit\n");
441     /* File extension should be unset */
442     ok(lpCacheEntryInfo2->lpszFileExtension == NULL,
443         "Fileextension isn't unset: %s\n",
444         lpCacheEntryInfo2->lpszFileExtension);
445     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
446     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo2);
447
448     ret = CommitUrlCacheEntry(test_url, filenameA, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL);
449     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
450
451     cbCacheEntryInfo = 0;
452     SetLastError(0xdeadbeef);
453     ret = RetrieveUrlCacheEntryFile(test_url, NULL, &cbCacheEntryInfo, 0);
454     ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n");
455     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
456        "RetrieveUrlCacheEntryFile should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError());
457
458     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
459     ret = RetrieveUrlCacheEntryFile(test_url, lpCacheEntryInfo, &cbCacheEntryInfo, 0);
460     ok(ret, "RetrieveUrlCacheEntryFile failed with error %d\n", GetLastError());
461
462     if (ret) check_cache_entry_infoA("RetrieveUrlCacheEntryFile", lpCacheEntryInfo);
463
464     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
465
466     cbCacheEntryInfo = 0;
467     SetLastError(0xdeadbeef);
468     ret = RetrieveUrlCacheEntryFile(test_url1, NULL, &cbCacheEntryInfo, 0);
469     ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n");
470     ok(GetLastError() == ERROR_INVALID_DATA,
471        "RetrieveUrlCacheEntryFile should have set last error to ERROR_INVALID_DATA instead of %d\n", GetLastError());
472
473     if (pUnlockUrlCacheEntryFileA)
474     {
475         ret = pUnlockUrlCacheEntryFileA(test_url, 0);
476         ok(ret, "UnlockUrlCacheEntryFileA failed with error %d\n", GetLastError());
477     }
478
479     /* test Find*UrlCacheEntry functions */
480     test_find_url_cache_entriesA();
481
482     test_GetUrlCacheEntryInfoExA();
483     test_RetrieveUrlCacheEntryA();
484     test_IsUrlCacheEntryExpiredA();
485
486     if (pDeleteUrlCacheEntryA)
487     {
488         ret = pDeleteUrlCacheEntryA(test_url);
489         ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError());
490         ret = pDeleteUrlCacheEntryA(test_url1);
491         ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError());
492     }
493
494     SetLastError(0xdeadbeef);
495     ret = DeleteFile(filenameA);
496     ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND, "local file should no longer exist\n");
497
498     /* Creating two entries with the same URL */
499     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA, 0);
500     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
501
502     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA1, 0);
503     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
504
505     ok(lstrcmpiA(filenameA, filenameA1), "expected a different file name\n");
506
507     create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte));
508     create_and_write_file(filenameA1, &zero_byte, sizeof(zero_byte));
509     check_file_exists(filenameA);
510     check_file_exists(filenameA1);
511
512     ret = CommitUrlCacheEntry(test_url, filenameA, filetime_zero,
513             filetime_zero, NORMAL_CACHE_ENTRY, (LPBYTE)ok_header,
514             strlen(ok_header), "html", NULL);
515     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
516     check_file_exists(filenameA);
517     check_file_exists(filenameA1);
518     ret = CommitUrlCacheEntry(test_url, filenameA1, filetime_zero,
519             filetime_zero, COOKIE_CACHE_ENTRY, NULL, 0, "html", NULL);
520     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
521     /* By committing the same URL a second time, the prior entry is
522      * overwritten...
523      */
524     cbCacheEntryInfo = 0;
525     SetLastError(0xdeadbeef);
526     ret = GetUrlCacheEntryInfo(test_url, NULL, &cbCacheEntryInfo);
527     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
528     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
529        "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
530     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
531     ret = GetUrlCacheEntryInfo(test_url, lpCacheEntryInfo, &cbCacheEntryInfo);
532     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
533     /* with the previous entry type retained.. */
534     ok(lpCacheEntryInfo->CacheEntryType & NORMAL_CACHE_ENTRY,
535        "expected cache entry type NORMAL_CACHE_ENTRY, got %d (0x%08x)\n",
536        lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType);
537     /* and the headers overwritten.. */
538     ok(!lpCacheEntryInfo->dwHeaderInfoSize, "expected headers size 0, got %d\n",
539        lpCacheEntryInfo->dwHeaderInfoSize);
540     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
541     /* and the previous filename shouldn't exist. */
542     check_file_not_exists(filenameA);
543     check_file_exists(filenameA1);
544
545     if (pDeleteUrlCacheEntryA)
546     {
547         ret = pDeleteUrlCacheEntryA(test_url);
548         ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError());
549         check_file_not_exists(filenameA);
550         check_file_not_exists(filenameA1);
551         /* Just in case, clean up files */
552         DeleteFileA(filenameA1);
553         DeleteFileA(filenameA);
554     }
555
556     /* Check whether a retrieved cache entry can be deleted before it's
557      * unlocked:
558      */
559     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA, 0);
560     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
561     ret = CommitUrlCacheEntry(test_url, filenameA, filetime_zero, filetime_zero,
562             NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL);
563     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
564
565     cbCacheEntryInfo = 0;
566     SetLastError(0xdeadbeef);
567     ret = RetrieveUrlCacheEntryFile(test_url, NULL, &cbCacheEntryInfo, 0);
568     ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n");
569     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
570        "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
571
572     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
573     ret = RetrieveUrlCacheEntryFile(test_url, lpCacheEntryInfo,
574             &cbCacheEntryInfo, 0);
575     ok(ret, "RetrieveUrlCacheEntryFile failed with error %d\n", GetLastError());
576
577     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
578
579     if (pDeleteUrlCacheEntryA)
580     {
581         ret = pDeleteUrlCacheEntryA(test_url);
582         ok(!ret, "Expected failure\n");
583         ok(GetLastError() == ERROR_SHARING_VIOLATION,
584            "Expected ERROR_SHARING_VIOLATION, got %d\n", GetLastError());
585         check_file_exists(filenameA);
586     }
587
588     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
589     memset(lpCacheEntryInfo, 0, cbCacheEntryInfo);
590     ret = GetUrlCacheEntryInfo(test_url, lpCacheEntryInfo, &cbCacheEntryInfo);
591     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
592     ok(lpCacheEntryInfo->CacheEntryType & 0x400000,
593         "CacheEntryType hasn't PENDING_DELETE_CACHE_ENTRY set, (flags %08x)\n",
594         lpCacheEntryInfo->CacheEntryType);
595     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
596
597     if (pUnlockUrlCacheEntryFileA)
598     {
599         check_file_exists(filenameA);
600         ret = pUnlockUrlCacheEntryFileA(test_url, 0);
601         ok(ret, "UnlockUrlCacheEntryFileA failed: %d\n", GetLastError());
602         /* By unlocking the already-deleted cache entry, the file associated
603          * with it is deleted..
604          */
605         check_file_not_exists(filenameA);
606         /* (just in case, delete file) */
607         DeleteFileA(filenameA);
608     }
609     if (pDeleteUrlCacheEntryA)
610     {
611         /* and a subsequent deletion should fail. */
612         ret = pDeleteUrlCacheEntryA(test_url);
613         ok(!ret, "Expected failure\n");
614         ok(GetLastError() == ERROR_FILE_NOT_FOUND,
615            "expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
616     }
617
618     /* Test whether preventing a file from being deleted causes
619      * DeleteUrlCacheEntryA to fail.
620      */
621     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA, 0);
622     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
623
624     create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte));
625     check_file_exists(filenameA);
626
627     ret = CommitUrlCacheEntry(test_url, filenameA, filetime_zero,
628             filetime_zero, NORMAL_CACHE_ENTRY, (LPBYTE)ok_header,
629             strlen(ok_header), "html", NULL);
630     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
631     check_file_exists(filenameA);
632     hFile = CreateFileA(filenameA, GENERIC_READ, 0, NULL, OPEN_EXISTING,
633             FILE_ATTRIBUTE_NORMAL, NULL);
634     ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA failed: %d\n",
635        GetLastError());
636     if (pDeleteUrlCacheEntryA)
637     {
638         /* DeleteUrlCacheEntryA should succeed.. */
639         ret = pDeleteUrlCacheEntryA(test_url);
640         ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError());
641     }
642     CloseHandle(hFile);
643     if (pDeleteUrlCacheEntryA)
644     {
645         /* and a subsequent deletion should fail.. */
646         ret = pDeleteUrlCacheEntryA(test_url);
647         ok(!ret, "Expected failure\n");
648         ok(GetLastError() == ERROR_FILE_NOT_FOUND,
649            "expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
650     }
651     /* and the file should be untouched. */
652     check_file_exists(filenameA);
653     DeleteFileA(filenameA);
654
655     /* Try creating a sticky entry.  Unlike non-sticky entries, the filename
656      * must have been set already.
657      */
658     SetLastError(0xdeadbeef);
659     ret = CommitUrlCacheEntry(test_url, NULL, filetime_zero, filetime_zero,
660             STICKY_CACHE_ENTRY, (LPBYTE)ok_header, strlen(ok_header), "html",
661             NULL);
662     ok(!ret, "expected failure\n");
663     ok(GetLastError() == ERROR_INVALID_PARAMETER,
664        "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
665     SetLastError(0xdeadbeef);
666     ret = CommitUrlCacheEntry(test_url, NULL, filetime_zero, filetime_zero,
667             NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY,
668             (LPBYTE)ok_header, strlen(ok_header), "html", NULL);
669     ok(!ret, "expected failure\n");
670     ok(GetLastError() == ERROR_INVALID_PARAMETER,
671        "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
672
673     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA, 0);
674     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
675     create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte));
676     ret = CommitUrlCacheEntry(test_url, filenameA, filetime_zero, filetime_zero,
677             NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY,
678             (LPBYTE)ok_header, strlen(ok_header), "html", NULL);
679     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
680     cbCacheEntryInfo = 0;
681     SetLastError(0xdeadbeef);
682     ret = GetUrlCacheEntryInfo(test_url, NULL, &cbCacheEntryInfo);
683     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
684     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
685        "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
686     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
687     ret = GetUrlCacheEntryInfo(test_url, lpCacheEntryInfo, &cbCacheEntryInfo);
688     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
689     ok(lpCacheEntryInfo->CacheEntryType & (NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY),
690        "expected cache entry type NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY, got %d (0x%08x)\n",
691        lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType);
692     ok(U(*lpCacheEntryInfo).dwExemptDelta == 86400,
693        "expected dwExemptDelta 86400, got %d\n",
694        U(*lpCacheEntryInfo).dwExemptDelta);
695     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
696     if (pDeleteUrlCacheEntryA)
697     {
698         ret = pDeleteUrlCacheEntryA(test_url);
699         ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError());
700         /* When explicitly deleting the cache entry, the file is also deleted */
701         check_file_not_exists(filenameA);
702     }
703     /* Test once again, setting the exempt delta via SetUrlCacheEntryInfo */
704     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA, 0);
705     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
706     create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte));
707     ret = CommitUrlCacheEntry(test_url, filenameA, filetime_zero, filetime_zero,
708             NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY,
709             (LPBYTE)ok_header, strlen(ok_header), "html", NULL);
710     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
711     cbCacheEntryInfo = 0;
712     SetLastError(0xdeadbeef);
713     ret = GetUrlCacheEntryInfo(test_url, NULL, &cbCacheEntryInfo);
714     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
715     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
716        "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
717     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
718     ret = GetUrlCacheEntryInfo(test_url, lpCacheEntryInfo, &cbCacheEntryInfo);
719     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
720     ok(lpCacheEntryInfo->CacheEntryType & (NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY),
721        "expected cache entry type NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY, got %d (0x%08x)\n",
722        lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType);
723     ok(U(*lpCacheEntryInfo).dwExemptDelta == 86400,
724        "expected dwExemptDelta 86400, got %d\n",
725        U(*lpCacheEntryInfo).dwExemptDelta);
726     U(*lpCacheEntryInfo).dwExemptDelta = 0;
727     ret = SetUrlCacheEntryInfoA(test_url, lpCacheEntryInfo,
728             CACHE_ENTRY_EXEMPT_DELTA_FC);
729     ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError());
730     ret = GetUrlCacheEntryInfo(test_url, lpCacheEntryInfo, &cbCacheEntryInfo);
731     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
732     ok(!U(*lpCacheEntryInfo).dwExemptDelta, "expected dwExemptDelta 0, got %d\n",
733        U(*lpCacheEntryInfo).dwExemptDelta);
734     /* See whether a sticky cache entry has the flag cleared once the exempt
735      * delta is meaningless.
736      */
737     ok(lpCacheEntryInfo->CacheEntryType & (NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY),
738        "expected cache entry type NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY, got %d (0x%08x)\n",
739        lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType);
740
741     /* Recommit of Url entry keeps dwExemptDelta */
742     U(*lpCacheEntryInfo).dwExemptDelta = 8600;
743     ret = SetUrlCacheEntryInfoA(test_url, lpCacheEntryInfo,
744             CACHE_ENTRY_EXEMPT_DELTA_FC);
745     ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError());
746
747     ret = CreateUrlCacheEntry(test_url, 0, "html", filenameA1, 0);
748     ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError());
749     create_and_write_file(filenameA1, &zero_byte, sizeof(zero_byte));
750
751     ret = CommitUrlCacheEntry(test_url, filenameA1, filetime_zero, filetime_zero,
752             NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY,
753             (LPBYTE)ok_header, strlen(ok_header), "html", NULL);
754     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
755
756     ret = GetUrlCacheEntryInfo(test_url, lpCacheEntryInfo, &cbCacheEntryInfo);
757     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
758     ok(U(*lpCacheEntryInfo).dwExemptDelta == 8600,
759        "expected dwExemptDelta 8600, got %d\n",
760        U(*lpCacheEntryInfo).dwExemptDelta);
761
762     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
763
764     if (pDeleteUrlCacheEntryA)
765     {
766         ret = pDeleteUrlCacheEntryA(test_url);
767         ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError());
768         check_file_not_exists(filenameA);
769     }
770
771     /* Test if files with identical hash keys are handled correctly */
772     ret = CommitUrlCacheEntryA(test_hash_collisions1, NULL, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL);
773     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
774     ret = CommitUrlCacheEntryA(test_hash_collisions2, NULL, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL);
775     ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError());
776
777     cbCacheEntryInfo = 0;
778     ret = GetUrlCacheEntryInfo(test_hash_collisions1, NULL, &cbCacheEntryInfo);
779     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
780     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
781             "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
782     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
783     ret = GetUrlCacheEntryInfo(test_hash_collisions1, lpCacheEntryInfo, &cbCacheEntryInfo);
784     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
785     ok(!strcmp(lpCacheEntryInfo->lpszSourceUrlName, test_hash_collisions1),
786             "got incorrect entry: %s\n", lpCacheEntryInfo->lpszSourceUrlName);
787     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
788
789     cbCacheEntryInfo = 0;
790     ret = GetUrlCacheEntryInfo(test_hash_collisions2, NULL, &cbCacheEntryInfo);
791     ok(!ret, "GetUrlCacheEntryInfo should have failed\n");
792     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
793             "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError());
794     lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo);
795     ret = GetUrlCacheEntryInfo(test_hash_collisions2, lpCacheEntryInfo, &cbCacheEntryInfo);
796     ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError());
797     ok(!strcmp(lpCacheEntryInfo->lpszSourceUrlName, test_hash_collisions2),
798             "got incorrect entry: %s\n", lpCacheEntryInfo->lpszSourceUrlName);
799     HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo);
800
801     if (pDeleteUrlCacheEntryA) {
802         ret = pDeleteUrlCacheEntryA(test_hash_collisions1);
803         ok(ret, "DeleteUrlCacheEntry failed: %d\n", GetLastError());
804         ret = pDeleteUrlCacheEntryA(test_hash_collisions2);
805         ok(ret, "DeleteUrlCacheEntry failed: %d\n", GetLastError());
806     }
807 }
808
809 static void test_FindCloseUrlCache(void)
810 {
811     BOOL r;
812     DWORD err;
813
814     SetLastError(0xdeadbeef);
815     r = FindCloseUrlCache(NULL);
816     err = GetLastError();
817     ok(0 == r, "expected 0, got %d\n", r);
818     ok(ERROR_INVALID_HANDLE == err, "expected %d, got %d\n", ERROR_INVALID_HANDLE, err);
819 }
820
821 static void test_GetDiskInfoA(void)
822 {
823     BOOL ret;
824     DWORD error, cluster_size;
825     DWORDLONG free, total;
826     char path[MAX_PATH], *p;
827
828     GetSystemDirectoryA(path, MAX_PATH);
829     if ((p = strchr(path, '\\'))) *++p = 0;
830
831     ret = GetDiskInfoA(path, &cluster_size, &free, &total);
832     ok(ret, "GetDiskInfoA failed %u\n", GetLastError());
833
834     ret = GetDiskInfoA(path, &cluster_size, &free, NULL);
835     ok(ret, "GetDiskInfoA failed %u\n", GetLastError());
836
837     ret = GetDiskInfoA(path, &cluster_size, NULL, NULL);
838     ok(ret, "GetDiskInfoA failed %u\n", GetLastError());
839
840     ret = GetDiskInfoA(path, NULL, NULL, NULL);
841     ok(ret, "GetDiskInfoA failed %u\n", GetLastError());
842
843     SetLastError(0xdeadbeef);
844     strcpy(p, "\\non\\existing\\path");
845     ret = GetDiskInfoA(path, NULL, NULL, NULL);
846     error = GetLastError();
847     ok(!ret ||
848        broken(ret), /* < IE7 */
849        "GetDiskInfoA succeeded\n");
850     ok(error == ERROR_PATH_NOT_FOUND ||
851        broken(error == 0xdeadbeef), /* < IE7 */
852        "got %u expected ERROR_PATH_NOT_FOUND\n", error);
853
854     SetLastError(0xdeadbeef);
855     ret = GetDiskInfoA(NULL, NULL, NULL, NULL);
856     error = GetLastError();
857     ok(!ret, "GetDiskInfoA succeeded\n");
858     ok(error == ERROR_INVALID_PARAMETER, "got %u expected ERROR_INVALID_PARAMETER\n", error);
859 }
860
861 START_TEST(urlcache)
862 {
863     HMODULE hdll;
864     hdll = GetModuleHandleA("wininet.dll");
865
866     if(!GetProcAddress(hdll, "InternetGetCookieExW")) {
867         win_skip("Too old IE (older than 6.0)\n");
868         return;
869     }
870
871     pDeleteUrlCacheEntryA = (void*)GetProcAddress(hdll, "DeleteUrlCacheEntryA");
872     pUnlockUrlCacheEntryFileA = (void*)GetProcAddress(hdll, "UnlockUrlCacheEntryFileA");
873     test_urlcacheA();
874     test_FindCloseUrlCache();
875     test_GetDiskInfoA();
876 }