diff --git a/rostests/winetests/wininet/CMakeLists.txt b/rostests/winetests/wininet/CMakeLists.txt index 562e435295f..e3a01707794 100644 --- a/rostests/winetests/wininet/CMakeLists.txt +++ b/rostests/winetests/wininet/CMakeLists.txt @@ -15,5 +15,5 @@ list(APPEND SOURCE add_executable(wininet_winetest ${SOURCE}) target_link_libraries(wininet_winetest wine) set_module_type(wininet_winetest win32cui) -add_importlibs(wininet_winetest wininet ws2_32 advapi32 msvcrt kernel32 ntdll) +add_importlibs(wininet_winetest wininet ws2_32 user32 advapi32 msvcrt kernel32 ntdll) add_cd_file(TARGET wininet_winetest DESTINATION reactos/bin FOR all) diff --git a/rostests/winetests/wininet/ftp.c b/rostests/winetests/wininet/ftp.c index df9c34753d6..a483dc9bbc7 100644 --- a/rostests/winetests/wininet/ftp.c +++ b/rostests/winetests/wininet/ftp.c @@ -943,12 +943,6 @@ static void test_status_callbacks(HINTERNET hInternet) HINTERNET hFtp; BOOL ret; - if (!pInternetSetStatusCallbackA) - { - win_skip("InternetSetStatusCallbackA() is not available, skipping test\n"); - return; - } - cb = pInternetSetStatusCallbackA(hInternet, status_callback); ok(cb == NULL, "expected NULL got %p\n", cb); @@ -973,6 +967,12 @@ START_TEST(ftp) HANDLE hInternet, hFtp, hHttp; hWininet = GetModuleHandleA("wininet.dll"); + + if(!GetProcAddress(hWininet, "InternetGetCookieExW")) { + win_skip("Too old IE (older than 6.0)\n"); + return; + } + pFtpCommandA = (void*)GetProcAddress(hWininet, "FtpCommandA"); pInternetSetStatusCallbackA = (void*)GetProcAddress(hWininet, "InternetSetStatusCallbackA"); diff --git a/rostests/winetests/wininet/http.c b/rostests/winetests/wininet/http.c index 93b3877a2b5..5db7f64c4fe 100644 --- a/rostests/winetests/wininet/http.c +++ b/rostests/winetests/wininet/http.c @@ -31,7 +31,7 @@ #include "wine/test.h" -#define TEST_URL "http://test.winehq.org/hello.html" +#define TEST_URL "http://test.winehq.org/tests/hello.html" static BOOL first_connection_to_test_url = TRUE; @@ -62,14 +62,14 @@ static BOOL first_connection_to_test_url = TRUE; if (!expect[status] && !optional[status] && wine_allow[status]) \ { \ todo_wine ok(expect[status], "unexpected status %d (%s)\n", status, \ - status < MAX_INTERNET_STATUS && status_string[status][0] != 0 ? \ + status < MAX_INTERNET_STATUS && status_string[status] ? \ status_string[status] : "unknown"); \ wine_allow[status]--; \ } \ else \ { \ ok(expect[status] || optional[status], "unexpected status %d (%s)\n", status, \ - status < MAX_INTERNET_STATUS && status_string[status][0] != 0 ? \ + status < MAX_INTERNET_STATUS && status_string[status] ? \ status_string[status] : "unknown"); \ if (expect[status]) expect[status]--; \ else optional[status]--; \ @@ -86,7 +86,7 @@ static BOOL first_connection_to_test_url = TRUE; do { \ ok(notified[status] + optional[status] == (num), \ "expected status %d (%s) %d times, received %d times\n", \ - status, status < MAX_INTERNET_STATUS && status_string[status][0] != 0 ? \ + status, status < MAX_INTERNET_STATUS && status_string[status] ? \ status_string[status] : "unknown", (num), notified[status]); \ CLEAR_NOTIFIED(status); \ }while(0) @@ -98,31 +98,43 @@ static BOOL first_connection_to_test_url = TRUE; CHECK_NOTIFIED2(status, 0) #define MAX_INTERNET_STATUS (INTERNET_STATUS_COOKIE_HISTORY+1) -#define MAX_STATUS_NAME 50 static int expect[MAX_INTERNET_STATUS], optional[MAX_INTERNET_STATUS], wine_allow[MAX_INTERNET_STATUS], notified[MAX_INTERNET_STATUS]; -static CHAR status_string[MAX_INTERNET_STATUS][MAX_STATUS_NAME]; +static const char *status_string[MAX_INTERNET_STATUS]; -static HANDLE hCompleteEvent; +static HANDLE hCompleteEvent, conn_close_event; #define TESTF_REDIRECT 0x01 #define TESTF_COMPRESSED 0x02 #define TESTF_ALLOW_COOKIE 0x04 +#define TESTF_CHUNKED 0x08 typedef struct { const char *url; const char *redirected_url; const char *host; const char *path; + const char *headers; DWORD flags; + const char *post_data; + const char *content; } test_data_t; static const test_data_t test_data[] = { { - "http://test.winehq.org/testredirect", - "http://test.winehq.org/hello.html", + "http://test.winehq.org/tests/data.php", + "http://test.winehq.org/tests/data.php", "test.winehq.org", - "/testredirect", + "/tests/data.php", + "", + TESTF_CHUNKED + }, + { + "http://test.winehq.org/tests/redirect", + "http://test.winehq.org/tests/hello.html", + "test.winehq.org", + "/tests/redirect", + "", TESTF_REDIRECT }, { @@ -130,12 +142,43 @@ static const test_data_t test_data[] = { "http://www.codeweavers.com/", "www.codeweavers.com", "", + "Accept-Encoding: gzip, deflate", TESTF_COMPRESSED|TESTF_ALLOW_COOKIE + }, + { + "http://test.winehq.org/tests/post.php", + "http://test.winehq.org/tests/post.php", + "test.winehq.org", + "/tests/post.php", + "Content-Type: application/x-www-form-urlencoded", + 0, + "mode=Test", + "mode => Test\n" } }; static INTERNET_STATUS_CALLBACK (WINAPI *pInternetSetStatusCallbackA)(HINTERNET ,INTERNET_STATUS_CALLBACK); +static BOOL proxy_active(void) +{ + HKEY internet_settings; + DWORD proxy_enable; + DWORD size; + + if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + 0, KEY_QUERY_VALUE, &internet_settings) != ERROR_SUCCESS) + return FALSE; + + size = sizeof(DWORD); + if (RegQueryValueExA(internet_settings, "ProxyEnable", NULL, NULL, (LPBYTE) &proxy_enable, &size) != ERROR_SUCCESS) + proxy_enable = 0; + + RegCloseKey(internet_settings); + + return proxy_enable != 0; +} + +static int close_handle_cnt; static VOID WINAPI callback( HINTERNET hInternet, @@ -235,6 +278,8 @@ static VOID WINAPI callback( trace("%04x:Callback %p 0x%lx INTERNET_STATUS_HANDLE_CLOSING %p %d\n", GetCurrentThreadId(), hInternet, dwContext, *(HINTERNET *)lpvStatusInformation, dwStatusInformationLength); + if(!--close_handle_cnt) + SetEvent(hCompleteEvent); break; case INTERNET_STATUS_REQUEST_COMPLETE: { @@ -268,11 +313,26 @@ static VOID WINAPI callback( } } -static void InternetReadFile_test(int flags, const test_data_t *test) +static void close_async_handle(HINTERNET handle, HANDLE complete_event, int handle_cnt) { BOOL res; + + close_handle_cnt = handle_cnt; + + SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, handle_cnt); + res = InternetCloseHandle(handle); + ok(res, "InternetCloseHandle failed: %u\n", GetLastError()); + WaitForSingleObject(hCompleteEvent, INFINITE); + CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, handle_cnt); + SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, handle_cnt); +} + +static void InternetReadFile_test(int flags, const test_data_t *test) +{ + char *post_data = NULL; + BOOL res, on_async = TRUE; CHAR buffer[4000]; - DWORD length; + DWORD length, post_len = 0; DWORD out; const char *types[2] = { "*", NULL }; HINTERNET hi, hic = 0, hor = 0; @@ -305,7 +365,7 @@ static void InternetReadFile_test(int flags, const test_data_t *test) SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); trace("HttpOpenRequestA <--\n"); - hor = HttpOpenRequestA(hic, "GET", test->path, NULL, NULL, types, + hor = HttpOpenRequestA(hic, test->post_data ? "POST" : "GET", test->path, NULL, NULL, types, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, 0xdeadbead); if (hor == 0x0 && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) { @@ -344,17 +404,8 @@ static void InternetReadFile_test(int flags, const test_data_t *test) { SET_EXPECT(INTERNET_STATUS_RESOLVING_NAME); SET_EXPECT(INTERNET_STATUS_NAME_RESOLVED); - SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME); - SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED); } - else - { - SET_WINE_ALLOW2(INTERNET_STATUS_RESOLVING_NAME,2); - SET_WINE_ALLOW2(INTERNET_STATUS_NAME_RESOLVED,2); - } - SET_WINE_ALLOW(INTERNET_STATUS_CONNECTING_TO_SERVER); SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER); - SET_WINE_ALLOW(INTERNET_STATUS_CONNECTED_TO_SERVER); SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER); SET_EXPECT2(INTERNET_STATUS_SENDING_REQUEST, (test->flags & TESTF_REDIRECT) ? 2 : 1); SET_EXPECT2(INTERNET_STATUS_REQUEST_SENT, (test->flags & TESTF_REDIRECT) ? 2 : 1); @@ -369,8 +420,6 @@ static void InternetReadFile_test(int flags, const test_data_t *test) SET_OPTIONAL(INTERNET_STATUS_CONNECTED_TO_SERVER); if (flags & INTERNET_FLAG_ASYNC) SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); - else - SET_WINE_ALLOW(INTERNET_STATUS_REQUEST_COMPLETE); if(test->flags & TESTF_COMPRESSED) { BOOL b = TRUE; @@ -383,8 +432,13 @@ static void InternetReadFile_test(int flags, const test_data_t *test) } trace("HttpSendRequestA -->\n"); + if(test->post_data) { + post_len = strlen(test->post_data); + post_data = HeapAlloc(GetProcessHeap(), 0, post_len); + memcpy(post_data, test->post_data, post_len); + } SetLastError(0xdeadbeef); - res = HttpSendRequestA(hor, (test->flags & TESTF_COMPRESSED) ? "Accept-Encoding: gzip, deflate" : "", -1, NULL, 0); + res = HttpSendRequestA(hor, test->headers, -1, post_data, post_len); if (flags & INTERNET_FLAG_ASYNC) ok(!res && (GetLastError() == ERROR_IO_PENDING), "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); @@ -395,6 +449,7 @@ static void InternetReadFile_test(int flags, const test_data_t *test) if (flags & INTERNET_FLAG_ASYNC) WaitForSingleObject(hCompleteEvent, INFINITE); + HeapFree(GetProcessHeap(), 0, post_data); if(test->flags & TESTF_ALLOW_COOKIE) { CLEAR_NOTIFIED(INTERNET_STATUS_COOKIE_SENT); @@ -402,10 +457,18 @@ static void InternetReadFile_test(int flags, const test_data_t *test) } if (first_connection_to_test_url) { - CHECK_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); - CHECK_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + if (! proxy_active()) + { + CHECK_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + else + { + CLEAR_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CLEAR_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } } - else todo_wine + else { CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); @@ -418,8 +481,6 @@ static void InternetReadFile_test(int flags, const test_data_t *test) CHECK_NOTIFIED(INTERNET_STATUS_REDIRECT); if (flags & INTERNET_FLAG_ASYNC) CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); - else - CHECK_NOT_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); /* Sent on WinXP only if first_connection_to_test_url is TRUE, on Win98 always sent */ CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTING_TO_SERVER); CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTED_TO_SERVER); @@ -474,9 +535,6 @@ static void InternetReadFile_test(int flags, const test_data_t *test) if (flags & INTERNET_FLAG_ASYNC) SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); res = InternetQueryDataAvailable(hor,&length,0x0,0x0); - ok(!(!res && length != 0),"InternetQueryDataAvailable failed with non-zero length\n"); - ok(res || ((flags & INTERNET_FLAG_ASYNC) && GetLastError() == ERROR_IO_PENDING), - "InternetQueryDataAvailable failed, error %d\n", GetLastError()); if (flags & INTERNET_FLAG_ASYNC) { if (res) @@ -485,11 +543,22 @@ static void InternetReadFile_test(int flags, const test_data_t *test) } else if (GetLastError() == ERROR_IO_PENDING) { + trace("PENDING\n"); + /* on some tests, InternetQueryDataAvailable returns non-zero length and ERROR_IO_PENDING */ + if(!(test->flags & TESTF_CHUNKED)) + ok(!length, "InternetQueryDataAvailable returned ERROR_IO_PENDING and %u length\n", length); WaitForSingleObject(hCompleteEvent, INFINITE); CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); continue; + }else { + ok(0, "InternetQueryDataAvailable failed: %u\n", GetLastError()); } + }else { + ok(res, "InternetQueryDataAvailable failed: %u\n", GetLastError()); } + trace("LENGTH %d\n", length); + if(test->flags & TESTF_CHUNKED) + ok(length <= 8192, "length = %d, expected <= 8192\n", length); if (length) { char *buffer; @@ -501,10 +570,14 @@ static void InternetReadFile_test(int flags, const test_data_t *test) trace("ReadFile -> %s %i\n",res?"TRUE":"FALSE",length); + if(test->content) + ok(!strcmp(buffer, test->content), "buffer = '%s', expected '%s'\n", buffer, test->content); HeapFree(GetProcessHeap(),0,buffer); - } - if (length == 0) + }else { + ok(!on_async, "Returned zero size in response to request complete\n"); break; + } + on_async = FALSE; } if(test->flags & TESTF_REDIRECT) { CHECK_NOTIFIED2(INTERNET_STATUS_CLOSING_CONNECTION, 2); @@ -512,44 +585,7 @@ static void InternetReadFile_test(int flags, const test_data_t *test) } abort: trace("aborting\n"); - SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); - if (hor != 0x0) { - SET_WINE_ALLOW(INTERNET_STATUS_CLOSING_CONNECTION); - SET_WINE_ALLOW(INTERNET_STATUS_CONNECTION_CLOSED); - SetLastError(0xdeadbeef); - trace("closing\n"); - res = InternetCloseHandle(hor); - ok (res, "InternetCloseHandle of handle opened by HttpOpenRequestA failed\n"); - SetLastError(0xdeadbeef); - res = InternetCloseHandle(hor); - ok (!res, "Double close of handle opened by HttpOpenRequestA succeeded\n"); - ok (GetLastError() == ERROR_INVALID_HANDLE, - "Double close of handle should have set ERROR_INVALID_HANDLE instead of %u\n", - GetLastError()); - } - /* We intentionally do not close the handle opened by InternetConnectA as this - * tickles bug #9479: native closes child internet handles when the parent handles - * are closed. This is verified below by checking that the number of - * INTERNET_STATUS_HANDLE_CLOSING notifications matches the number expected. */ - if (hi != 0x0) { - SET_WINE_ALLOW(INTERNET_STATUS_HANDLE_CLOSING); - trace("closing 2\n"); - res = InternetCloseHandle(hi); - ok (res, "InternetCloseHandle of handle opened by InternetOpenA failed\n"); - if (flags & INTERNET_FLAG_ASYNC) - Sleep(100); - } - CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); - if (hor != 0x0) todo_wine - { - CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); - CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); - } - else - { - CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); - CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); - } + close_async_handle(hi, hCompleteEvent, 2); CloseHandle(hCompleteEvent); first_connection_to_test_url = FALSE; } @@ -580,7 +616,7 @@ static void InternetReadFile_chunked_test(void) if (hic == 0x0) goto abort; trace("HttpOpenRequestA <--\n"); - hor = HttpOpenRequestA(hic, "GET", "/testchunked", NULL, NULL, types, + hor = HttpOpenRequestA(hic, "GET", "/tests/chunked", NULL, NULL, types, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, 0xdeadbead); if (hor == 0x0 && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) { @@ -614,8 +650,10 @@ static void InternetReadFile_chunked_test(void) res = HttpQueryInfoA(hor,HTTP_QUERY_TRANSFER_ENCODING,buffer,&length,0x0); buffer[length]=0; trace("Option TRANSFER_ENCODING -> %i %s\n",res,buffer); - ok( res, "Failed to get TRANSFER_ENCODING option, error %u\n", GetLastError() ); - ok( !strcmp( buffer, "chunked" ), "Wrong transfer encoding '%s'\n", buffer ); + ok( res || ( proxy_active() && GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND ), + "Failed to get TRANSFER_ENCODING option, error %u\n", GetLastError() ); + ok( !strcmp( buffer, "chunked" ) || ( ! res && proxy_active() && GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND ), + "Wrong transfer encoding '%s'\n", buffer ); SetLastError( 0xdeadbeef ); length = 16; @@ -697,7 +735,7 @@ static void InternetReadFileExA_test(int flags) SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); trace("HttpOpenRequestA <--\n"); - hor = HttpOpenRequestA(hic, "GET", "/testredirect", NULL, NULL, types, + hor = HttpOpenRequestA(hic, "GET", "/tests/redirect", NULL, NULL, types, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, 0xdeadbead); if (hor == 0x0 && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) { @@ -721,17 +759,8 @@ static void InternetReadFileExA_test(int flags) { SET_EXPECT(INTERNET_STATUS_RESOLVING_NAME); SET_EXPECT(INTERNET_STATUS_NAME_RESOLVED); - SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME); - SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED); } - else - { - SET_WINE_ALLOW2(INTERNET_STATUS_RESOLVING_NAME,2); - SET_WINE_ALLOW2(INTERNET_STATUS_NAME_RESOLVED,2); - } - SET_WINE_ALLOW(INTERNET_STATUS_CONNECTING_TO_SERVER); SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER); - SET_WINE_ALLOW(INTERNET_STATUS_CONNECTED_TO_SERVER); SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER); SET_EXPECT2(INTERNET_STATUS_SENDING_REQUEST, 2); SET_EXPECT2(INTERNET_STATUS_REQUEST_SENT, 2); @@ -766,7 +795,7 @@ static void InternetReadFileExA_test(int flags) CHECK_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); CHECK_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); } - else todo_wine + else { CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); @@ -810,8 +839,6 @@ static void InternetReadFileExA_test(int flags) inetbuffers.dwOffsetLow = 5678; SET_EXPECT(INTERNET_STATUS_RECEIVING_RESPONSE); SET_EXPECT(INTERNET_STATUS_RESPONSE_RECEIVED); - SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); - SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); rc = InternetReadFileEx(hor, &inetbuffers, 0, 0xdeadcafe); ok(rc, "InternetReadFileEx failed with error %u\n", GetLastError()); trace("read %i bytes\n", inetbuffers.dwBufferLength); @@ -829,8 +856,6 @@ static void InternetReadFileExA_test(int flags) length = 0; trace("Entering Query loop\n"); - SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); - SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); while (TRUE) { inetbuffers.dwStructSize = sizeof(INTERNET_BUFFERS); @@ -841,8 +866,6 @@ static void InternetReadFileExA_test(int flags) SET_WINE_ALLOW(INTERNET_STATUS_RECEIVING_RESPONSE); SET_WINE_ALLOW(INTERNET_STATUS_RESPONSE_RECEIVED); - SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); - SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); rc = InternetReadFileExA(hor, &inetbuffers, IRF_ASYNC | IRF_USE_CONTEXT, 0xcafebabe); if (!rc) @@ -899,37 +922,8 @@ static void InternetReadFileExA_test(int flags) ok(length > 0, "failed to read any of the document\n"); trace("Finished. Read %d bytes\n", length); - /* WinXP does not send, but Win98 does */ - CLEAR_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); - CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); abort: - SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); - if (hor) { - SET_WINE_ALLOW(INTERNET_STATUS_CLOSING_CONNECTION); - SET_WINE_ALLOW(INTERNET_STATUS_CONNECTION_CLOSED); - rc = InternetCloseHandle(hor); - ok ((rc != 0), "InternetCloseHandle of handle opened by HttpOpenRequestA failed\n"); - rc = InternetCloseHandle(hor); - ok ((rc == 0), "Double close of handle opened by HttpOpenRequestA succeeded\n"); - } - if (hic) { - rc = InternetCloseHandle(hic); - ok ((rc != 0), "InternetCloseHandle of handle opened by InternetConnectA failed\n"); - } - if (hi) { - SET_WINE_ALLOW(INTERNET_STATUS_HANDLE_CLOSING); - rc = InternetCloseHandle(hi); - ok ((rc != 0), "InternetCloseHandle of handle opened by InternetOpenA failed\n"); - if (flags & INTERNET_FLAG_ASYNC) - Sleep(100); - CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); - } - /* to enable once Wine is fixed to never send it - CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); - CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); - */ - CLEAR_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); - CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + close_async_handle(hi, hCompleteEvent, 2); CloseHandle(hCompleteEvent); first_connection_to_test_url = FALSE; } @@ -1024,7 +1018,7 @@ static void HttpSendRequestEx_test(void) szBuffer[dwBytesRead] = 0; ok(dwBytesRead == 13,"Read %u bytes instead of 13\n",dwBytesRead); - ok(strncmp(szBuffer,"mode => Test\n",dwBytesRead)==0,"Got string %s\n",szBuffer); + ok(strncmp(szBuffer,"mode => Test\n",dwBytesRead)==0 || broken(proxy_active()),"Got string %s\n",szBuffer); ok(InternetCloseHandle(hRequest), "Close request handle failed\n"); done: @@ -1099,7 +1093,7 @@ static void test_http_cache(void) INTERNET_SERVICE_HTTP, 0, 0); ok(connect != NULL, "Unable to connect to http://test.winehq.org with error %d\n", GetLastError()); - request = HttpOpenRequestA(connect, NULL, "/hello.html", NULL, NULL, types, INTERNET_FLAG_NEED_FILE, 0); + request = HttpOpenRequestA(connect, NULL, "/tests/hello.html", NULL, NULL, types, INTERNET_FLAG_NEED_FILE, 0); if (!request && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) { skip( "Network unreachable, skipping test\n" ); @@ -1114,7 +1108,7 @@ static void test_http_cache(void) size = sizeof(url); ret = InternetQueryOptionA(request, INTERNET_OPTION_URL, url, &size); ok(ret, "InternetQueryOptionA(INTERNET_OPTION_URL) failed: %u\n", GetLastError()); - ok(!strcmp(url, "http://test.winehq.org/hello.html"), "Wrong URL %s\n", url); + ok(!strcmp(url, "http://test.winehq.org/tests/hello.html"), "Wrong URL %s\n", url); size = sizeof(file_name); ret = InternetQueryOptionA(request, INTERNET_OPTION_DATAFILE_NAME, file_name, &size); @@ -1151,6 +1145,19 @@ static void test_http_cache(void) ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError()); CloseHandle(file); + /* Send the same request, requiring it to be retrieved from the cache */ + request = HttpOpenRequest(connect, "GET", "/tests/hello.html", NULL, NULL, NULL, INTERNET_FLAG_FROM_CACHE, 0); + + ret = HttpSendRequest(request, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed\n"); + + size = sizeof(buf); + ret = InternetReadFile(request, buf, sizeof(buf), &size); + ok(ret, "InternetReadFile failed: %u\n", GetLastError()); + ok(size == 100, "size = %u\n", size); + + ok(InternetCloseHandle(request), "Close request handle failed\n"); + request = HttpOpenRequestA(connect, NULL, "/", NULL, NULL, types, INTERNET_FLAG_NO_CACHE_WRITE, 0); ok(request != NULL, "Failed to open request handle err %u\n", GetLastError()); @@ -1551,6 +1558,7 @@ static const char page1[] = struct server_info { HANDLE hEvent; int port; + int num_testH_retrievals; }; static DWORD CALLBACK server_thread(LPVOID param) @@ -1741,6 +1749,29 @@ static DWORD CALLBACK server_thread(LPVOID param) { send(c, page1, sizeof page1-1, 0); } + if (strstr(buffer, "GET /testH")) + { + si->num_testH_retrievals++; + if (!strstr(buffer, "Content-Length: 0")) + { + send(c, okmsg, sizeof okmsg-1, 0); + send(c, page1, sizeof page1-1, 0); + } + else + send(c, notokmsg, sizeof notokmsg-1, 0); + } + if (strstr(buffer, "GET /test_no_content")) + { + static const char nocontentmsg[] = "HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n"; + send(c, nocontentmsg, sizeof(nocontentmsg)-1, 0); + } + if (strstr(buffer, "GET /test_conn_close")) + { + static const char conn_close_response[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nsome content"; + send(c, conn_close_response, sizeof(conn_close_response)-1, 0); + WaitForSingleObject(conn_close_event, INFINITE); + trace("closing connection\n"); + } shutdown(c, 2); closesocket(c); @@ -1771,10 +1802,11 @@ static void test_basic_request(int port, const char *verb, const char *url) count = 0; memset(buffer, 0, sizeof buffer); + SetLastError(0xdeadbeef); r = InternetReadFile(hr, buffer, sizeof buffer, &count); - ok(r, "InternetReadFile failed\n"); + ok(r, "InternetReadFile failed %u\n", GetLastError()); ok(count == sizeof page1 - 1, "count was wrong\n"); - ok(!memcmp(buffer, page1, sizeof page1), "http data wrong\n"); + ok(!memcmp(buffer, page1, sizeof page1), "http data wrong, got: %s\n", buffer); InternetCloseHandle(hr); InternetCloseHandle(hc); @@ -2092,6 +2124,141 @@ static void test_http1_1(int port) InternetCloseHandle(ses); } +static void test_no_content(int port) +{ + HINTERNET session, connection, req; + DWORD res; + + trace("Testing 204 no content response...\n"); + + hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + session = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC); + ok(session != NULL,"InternetOpen failed with error %u\n", GetLastError()); + + pInternetSetStatusCallbackA(session, callback); + + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + connection = InternetConnectA(session, "localhost", port, + NULL, NULL, INTERNET_SERVICE_HTTP, 0x0, 0xdeadbeef); + ok(connection != NULL,"InternetConnect failed with error %u\n", GetLastError()); + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + req = HttpOpenRequestA(connection, "GET", "/test_no_content", NULL, NULL, NULL, + INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, 0xdeadbead); + ok(req != NULL, "HttpOpenRequest failed: %u\n", GetLastError()); + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + + SET_OPTIONAL(INTERNET_STATUS_COOKIE_SENT); + SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_SENDING_REQUEST); + SET_EXPECT(INTERNET_STATUS_REQUEST_SENT); + SET_EXPECT(INTERNET_STATUS_RECEIVING_RESPONSE); + SET_EXPECT(INTERNET_STATUS_RESPONSE_RECEIVED); + SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); + SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + + res = HttpSendRequestA(req, NULL, -1, NULL, 0); + ok(!res && (GetLastError() == ERROR_IO_PENDING), + "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); + WaitForSingleObject(hCompleteEvent, INFINITE); + + CLEAR_NOTIFIED(INTERNET_STATUS_COOKIE_SENT); + CHECK_NOTIFIED(INTERNET_STATUS_CONNECTING_TO_SERVER); + CHECK_NOTIFIED(INTERNET_STATUS_CONNECTED_TO_SERVER); + CHECK_NOTIFIED(INTERNET_STATUS_SENDING_REQUEST); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_SENT); + CHECK_NOTIFIED(INTERNET_STATUS_RECEIVING_RESPONSE); + CHECK_NOTIFIED(INTERNET_STATUS_RESPONSE_RECEIVED); + CHECK_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CHECK_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + + close_async_handle(session, hCompleteEvent, 2); + CloseHandle(hCompleteEvent); +} + +static void test_conn_close(int port) +{ + HINTERNET session, connection, req; + DWORD res, avail, size; + BYTE buf[1024]; + + trace("Testing connection close connection...\n"); + + hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + conn_close_event = CreateEvent(NULL, FALSE, FALSE, NULL); + + session = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC); + ok(session != NULL,"InternetOpen failed with error %u\n", GetLastError()); + + pInternetSetStatusCallbackA(session, callback); + + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + connection = InternetConnectA(session, "localhost", port, + NULL, NULL, INTERNET_SERVICE_HTTP, 0x0, 0xdeadbeef); + ok(connection != NULL,"InternetConnect failed with error %u\n", GetLastError()); + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + req = HttpOpenRequestA(connection, "GET", "/test_conn_close", NULL, NULL, NULL, + INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, 0xdeadbead); + ok(req != NULL, "HttpOpenRequest failed: %u\n", GetLastError()); + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + + SET_OPTIONAL(INTERNET_STATUS_COOKIE_SENT); + SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_SENDING_REQUEST); + SET_EXPECT(INTERNET_STATUS_REQUEST_SENT); + SET_EXPECT(INTERNET_STATUS_RECEIVING_RESPONSE); + SET_EXPECT(INTERNET_STATUS_RESPONSE_RECEIVED); + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + + res = HttpSendRequestA(req, NULL, -1, NULL, 0); + ok(!res && (GetLastError() == ERROR_IO_PENDING), + "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); + WaitForSingleObject(hCompleteEvent, INFINITE); + + CLEAR_NOTIFIED(INTERNET_STATUS_COOKIE_SENT); + CHECK_NOTIFIED(INTERNET_STATUS_CONNECTING_TO_SERVER); + CHECK_NOTIFIED(INTERNET_STATUS_CONNECTED_TO_SERVER); + CHECK_NOTIFIED(INTERNET_STATUS_SENDING_REQUEST); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_SENT); + CHECK_NOTIFIED(INTERNET_STATUS_RECEIVING_RESPONSE); + CHECK_NOTIFIED(INTERNET_STATUS_RESPONSE_RECEIVED); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + + avail = 0; + res = InternetQueryDataAvailable(req, &avail, 0, 0); + ok(res, "InternetQueryDataAvailable failed: %u\n", GetLastError()); + ok(avail != 0, "avail = 0\n"); + + size = 0; + res = InternetReadFile(req, buf, avail, &size); + ok(res, "InternetReadFile failed: %u\n", GetLastError()); + + res = InternetQueryDataAvailable(req, &avail, 0, 0); + ok(!res && (GetLastError() == ERROR_IO_PENDING), + "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); + ok(!avail, "avail = %u, expected 0\n", avail); + + SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); + SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + SetEvent(conn_close_event); + WaitForSingleObject(hCompleteEvent, INFINITE); + CHECK_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CHECK_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + + close_async_handle(session, hCompleteEvent, 2); + CloseHandle(hCompleteEvent); +} + static void test_HttpSendRequestW(int port) { static const WCHAR header[] = {'U','A','-','C','P','U',':',' ','x','8','6',0}; @@ -2523,6 +2690,70 @@ static void test_options(int port) InternetCloseHandle(ses); } +static void test_url_caching(int port, int *num_retrievals) +{ + HINTERNET hi, hc, hr; + DWORD r, count; + char buffer[0x100]; + + ok(*num_retrievals == 0, "expected 0 retrievals prior to test\n"); + + hi = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + ok(hi != NULL, "open failed\n"); + + hc = InternetConnect(hi, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(hc != NULL, "connect failed\n"); + + /* Pre-load the cache: */ + hr = HttpOpenRequest(hc, "GET", "/testH", NULL, NULL, NULL, 0, 0); + ok(hr != NULL, "HttpOpenRequest failed\n"); + + r = HttpSendRequest(hr, NULL, 0, NULL, 0); + ok(r, "HttpSendRequest failed\n"); + + ok(*num_retrievals == 1, "expected 1 retrievals, got %d\n", *num_retrievals); + + count = 0; + memset(buffer, 0, sizeof buffer); + SetLastError(0xdeadbeef); + r = InternetReadFile(hr, buffer, sizeof buffer, &count); + ok(r, "InternetReadFile failed %u\n", GetLastError()); + ok(count == sizeof page1 - 1, "count was wrong\n"); + ok(!memcmp(buffer, page1, sizeof page1), "http data wrong\n"); + + InternetCloseHandle(hr); + + /* Send the same request, requiring it to be retrieved from the cache */ + hr = HttpOpenRequest(hc, "GET", "/testH", NULL, NULL, NULL, INTERNET_FLAG_FROM_CACHE, 0); + ok(hr != NULL, "HttpOpenRequest failed\n"); + + r = HttpSendRequest(hr, NULL, 0, NULL, 0); + /* Older Windows versions succeed with this request, newer ones fail with + * ERROR_FILE_NOT_FOUND. Accept either, as the older version allows us + * to verify that the server isn't contacted. + */ + if (!r) + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError()); + else + { + /* The server shouldn't be contacted for this request. */ + todo_wine + ok(*num_retrievals == 1, "expected 1 retrievals\n"); + + count = 0; + memset(buffer, 0, sizeof buffer); + SetLastError(0xdeadbeef); + r = InternetReadFile(hr, buffer, sizeof buffer, &count); + ok(r, "InternetReadFile failed %u\n", GetLastError()); + ok(count == sizeof page1 - 1, "count was wrong\n"); + ok(!memcmp(buffer, page1, sizeof page1), "http data wrong\n"); + } + + InternetCloseHandle(hc); + InternetCloseHandle(hi); +} + static void test_http_connection(void) { struct server_info si; @@ -2531,6 +2762,7 @@ static void test_http_connection(void) si.hEvent = CreateEvent(NULL, 0, 0, NULL); si.port = 7531; + si.num_testH_retrievals = 0; hThread = CreateThread(NULL, 0, server_thread, (LPVOID) &si, 0, &id); ok( hThread != NULL, "create thread failed\n"); @@ -2559,6 +2791,9 @@ static void test_http_connection(void) test_HttpSendRequestW(si.port); test_last_error(si.port); test_options(si.port); + test_url_caching(si.port, &si.num_testH_retrievals); + test_no_content(si.port); + test_conn_close(si.port); /* send the basic request again to shutdown the server thread */ test_basic_request(si.port, "GET", "/quit"); @@ -2568,6 +2803,195 @@ static void test_http_connection(void) CloseHandle(hThread); } +static void release_cert_info(INTERNET_CERTIFICATE_INFOA *info) +{ + LocalFree(info->lpszSubjectInfo); + LocalFree(info->lpszIssuerInfo); + LocalFree(info->lpszProtocolName); + LocalFree(info->lpszSignatureAlgName); + LocalFree(info->lpszEncryptionAlgName); +} + +static void test_secure_connection(void) +{ + static const WCHAR gizmo5[] = {'G','i','z','m','o','5',0}; + static const WCHAR testbot[] = {'t','e','s','t','b','o','t','.','w','i','n','e','h','q','.','o','r','g',0}; + static const WCHAR get[] = {'G','E','T',0}; + static const WCHAR slash[] = {'/',0}; + HINTERNET ses, con, req; + DWORD size, flags; + INTERNET_CERTIFICATE_INFOA *certificate_structA = NULL; + INTERNET_CERTIFICATE_INFOW *certificate_structW = NULL; + BOOL ret; + + ses = InternetOpen("Gizmo5", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + ok(ses != NULL, "InternetOpen failed\n"); + + con = InternetConnect(ses, "testbot.winehq.org", + INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, + INTERNET_SERVICE_HTTP, 0, 0); + ok(con != NULL, "InternetConnect failed\n"); + + req = HttpOpenRequest(con, "GET", "/", NULL, NULL, NULL, + INTERNET_FLAG_SECURE, 0); + ok(req != NULL, "HttpOpenRequest failed\n"); + + ret = HttpSendRequest(req, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed: %d\n", GetLastError()); + + size = sizeof(flags); + ret = InternetQueryOption(req, INTERNET_OPTION_SECURITY_FLAGS, &flags, &size); + ok(ret, "InternetQueryOption failed: %d\n", GetLastError()); + ok(flags & SECURITY_FLAG_SECURE, "expected secure flag to be set\n"); + + ret = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + NULL, &size); + ok(ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == sizeof(INTERNET_CERTIFICATE_INFOA), "size = %d\n", size); + certificate_structA = HeapAlloc(GetProcessHeap(), 0, size); + ret = InternetQueryOption(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + certificate_structA, &size); + ok(ret, "InternetQueryOption failed: %d\n", GetLastError()); + if (ret) + { + ok(certificate_structA->lpszSubjectInfo && + strlen(certificate_structA->lpszSubjectInfo) > 1, + "expected a non-empty subject name\n"); + ok(certificate_structA->lpszIssuerInfo && + strlen(certificate_structA->lpszIssuerInfo) > 1, + "expected a non-empty issuer name\n"); + ok(!certificate_structA->lpszSignatureAlgName, + "unexpected signature algorithm name\n"); + ok(!certificate_structA->lpszEncryptionAlgName, + "unexpected encryption algorithm name\n"); + ok(!certificate_structA->lpszProtocolName, + "unexpected protocol name\n"); + ok(certificate_structA->dwKeySize, "expected a non-zero key size\n"); + release_cert_info(certificate_structA); + } + HeapFree(GetProcessHeap(), 0, certificate_structA); + + /* Querying the same option through InternetQueryOptionW still results in + * ASCII strings being returned. + */ + size = 0; + ret = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + NULL, &size); + ok(ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == sizeof(INTERNET_CERTIFICATE_INFOW), "size = %d\n", size); + certificate_structW = HeapAlloc(GetProcessHeap(), 0, size); + ret = InternetQueryOption(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + certificate_structW, &size); + certificate_structA = (INTERNET_CERTIFICATE_INFOA *)certificate_structW; + ok(ret, "InternetQueryOption failed: %d\n", GetLastError()); + if (ret) + { + ok(certificate_structA->lpszSubjectInfo && + strlen(certificate_structA->lpszSubjectInfo) > 1, + "expected a non-empty subject name\n"); + ok(certificate_structA->lpszIssuerInfo && + strlen(certificate_structA->lpszIssuerInfo) > 1, + "expected a non-empty issuer name\n"); + ok(!certificate_structA->lpszSignatureAlgName, + "unexpected signature algorithm name\n"); + ok(!certificate_structA->lpszEncryptionAlgName, + "unexpected encryption algorithm name\n"); + ok(!certificate_structA->lpszProtocolName, + "unexpected protocol name\n"); + ok(certificate_structA->dwKeySize, "expected a non-zero key size\n"); + release_cert_info(certificate_structA); + } + HeapFree(GetProcessHeap(), 0, certificate_structW); + + InternetCloseHandle(req); + InternetCloseHandle(con); + InternetCloseHandle(ses); + + /* Repeating the tests with the W functions has the same result: */ + ses = InternetOpenW(gizmo5, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + ok(ses != NULL, "InternetOpen failed\n"); + + con = InternetConnectW(ses, testbot, + INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, + INTERNET_SERVICE_HTTP, 0, 0); + ok(con != NULL, "InternetConnect failed\n"); + + req = HttpOpenRequestW(con, get, slash, NULL, NULL, NULL, + INTERNET_FLAG_SECURE, 0); + ok(req != NULL, "HttpOpenRequest failed\n"); + + ret = HttpSendRequest(req, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed: %d\n", GetLastError()); + + size = sizeof(flags); + ret = InternetQueryOption(req, INTERNET_OPTION_SECURITY_FLAGS, &flags, &size); + ok(ret, "InternetQueryOption failed: %d\n", GetLastError()); + ok(flags & SECURITY_FLAG_SECURE, "expected secure flag to be set\n"); + + ret = InternetQueryOptionA(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + NULL, &size); + ok(ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == sizeof(INTERNET_CERTIFICATE_INFOA), "size = %d\n", size); + certificate_structA = HeapAlloc(GetProcessHeap(), 0, size); + ret = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + certificate_structA, &size); + ok(ret, "InternetQueryOption failed: %d\n", GetLastError()); + if (ret) + { + ok(certificate_structA->lpszSubjectInfo && + strlen(certificate_structA->lpszSubjectInfo) > 1, + "expected a non-empty subject name\n"); + ok(certificate_structA->lpszIssuerInfo && + strlen(certificate_structA->lpszIssuerInfo) > 1, + "expected a non-empty issuer name\n"); + ok(!certificate_structA->lpszSignatureAlgName, + "unexpected signature algorithm name\n"); + ok(!certificate_structA->lpszEncryptionAlgName, + "unexpected encryption algorithm name\n"); + ok(!certificate_structA->lpszProtocolName, + "unexpected protocol name\n"); + ok(certificate_structA->dwKeySize, "expected a non-zero key size\n"); + release_cert_info(certificate_structA); + } + HeapFree(GetProcessHeap(), 0, certificate_structA); + + /* Again, querying the same option through InternetQueryOptionW still + * results in ASCII strings being returned. + */ + size = 0; + ret = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + NULL, &size); + ok(ret || GetLastError() == ERROR_INSUFFICIENT_BUFFER, "InternetQueryOption failed: %d\n", GetLastError()); + ok(size == sizeof(INTERNET_CERTIFICATE_INFOW), "size = %d\n", size); + certificate_structW = HeapAlloc(GetProcessHeap(), 0, size); + ret = InternetQueryOptionW(req, INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT, + certificate_structW, &size); + certificate_structA = (INTERNET_CERTIFICATE_INFOA *)certificate_structW; + ok(ret, "InternetQueryOption failed: %d\n", GetLastError()); + if (ret) + { + ok(certificate_structA->lpszSubjectInfo && + strlen(certificate_structA->lpszSubjectInfo) > 1, + "expected a non-empty subject name\n"); + ok(certificate_structA->lpszIssuerInfo && + strlen(certificate_structA->lpszIssuerInfo) > 1, + "expected a non-empty issuer name\n"); + ok(!certificate_structA->lpszSignatureAlgName, + "unexpected signature algorithm name\n"); + ok(!certificate_structA->lpszEncryptionAlgName, + "unexpected encryption algorithm name\n"); + ok(!certificate_structA->lpszProtocolName, + "unexpected protocol name\n"); + ok(certificate_structA->dwKeySize, "expected a non-zero key size\n"); + release_cert_info(certificate_structA); + } + HeapFree(GetProcessHeap(), 0, certificate_structW); + + InternetCloseHandle(req); + InternetCloseHandle(con); + InternetCloseHandle(ses); +} + static void test_user_agent_header(void) { HINTERNET ses, con, req; @@ -2575,13 +2999,13 @@ static void test_user_agent_header(void) char buffer[64]; BOOL ret; - ses = InternetOpen("Gizmo5", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + ses = InternetOpen("Gizmo5", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); ok(ses != NULL, "InternetOpen failed\n"); con = InternetConnect(ses, "test.winehq.org", 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); ok(con != NULL, "InternetConnect failed\n"); - req = HttpOpenRequest(con, "GET", "/hello.html", "HTTP/1.0", NULL, NULL, 0, 0); + req = HttpOpenRequest(con, "GET", "/tests/hello.html", "HTTP/1.0", NULL, NULL, 0, 0); ok(req != NULL, "HttpOpenRequest failed\n"); size = sizeof(buffer); @@ -2827,8 +3251,8 @@ static const struct notification async_send_request_ex_test[] = { internet_connect, INTERNET_STATUS_HANDLE_CREATED, 0 }, { http_open_request, INTERNET_STATUS_HANDLE_CREATED, 0 }, { http_send_request_ex, INTERNET_STATUS_DETECTING_PROXY, 1, 0, 1 }, - { http_send_request_ex, INTERNET_STATUS_RESOLVING_NAME, 1 }, - { http_send_request_ex, INTERNET_STATUS_NAME_RESOLVED, 1 }, + { http_send_request_ex, INTERNET_STATUS_RESOLVING_NAME, 1, 0, 1 }, + { http_send_request_ex, INTERNET_STATUS_NAME_RESOLVED, 1, 0, 1 }, { http_send_request_ex, INTERNET_STATUS_CONNECTING_TO_SERVER, 1 }, { http_send_request_ex, INTERNET_STATUS_CONNECTED_TO_SERVER, 1 }, { http_send_request_ex, INTERNET_STATUS_SENDING_REQUEST, 1 }, @@ -2839,8 +3263,10 @@ static const struct notification async_send_request_ex_test[] = { http_end_request, INTERNET_STATUS_RECEIVING_RESPONSE, 1 }, { http_end_request, INTERNET_STATUS_RESPONSE_RECEIVED, 1 }, { http_end_request, INTERNET_STATUS_REQUEST_COMPLETE, 1 }, - { internet_close_handle, INTERNET_STATUS_HANDLE_CLOSING, 0, 1 }, - { internet_close_handle, INTERNET_STATUS_HANDLE_CLOSING, 0, 1 } + { internet_close_handle, INTERNET_STATUS_CLOSING_CONNECTION, 0, 0, 1 }, + { internet_close_handle, INTERNET_STATUS_CONNECTION_CLOSED, 0, 0, 1 }, + { internet_close_handle, INTERNET_STATUS_HANDLE_CLOSING, 0, }, + { internet_close_handle, INTERNET_STATUS_HANDLE_CLOSING, 0, } }; static void test_async_HttpSendRequestEx(void) @@ -2934,11 +3360,74 @@ static void test_async_HttpSendRequestEx(void) CloseHandle( info.wait ); } -#define STATUS_STRING(status) \ - memcpy(status_string[status], #status, sizeof(CHAR) * \ - (strlen(#status) < MAX_STATUS_NAME ? \ - strlen(#status) : \ - MAX_STATUS_NAME - 1)) +static HINTERNET closetest_session, closetest_req, closetest_conn; +static BOOL closetest_closed; + +static void WINAPI closetest_callback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, + LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) +{ + DWORD len, type; + BOOL res; + + trace("closetest_callback %p: %d\n", hInternet, dwInternetStatus); + + ok(hInternet == closetest_session || hInternet == closetest_conn || hInternet == closetest_req, + "Unexpected hInternet %p\n", hInternet); + if(!closetest_closed) + return; + + len = sizeof(type); + res = InternetQueryOptionA(closetest_req, INTERNET_OPTION_HANDLE_TYPE, &type, &len); + ok(!res && GetLastError() == ERROR_INVALID_HANDLE, + "InternetQueryOptionA(%p INTERNET_OPTION_HANDLE_TYPE) failed: %x %u, expected TRUE ERROR_INVALID_HANDLE\n", + closetest_req, res, GetLastError()); +} + +static void test_InternetCloseHandle(void) +{ + DWORD len, flags; + BOOL res; + + closetest_session = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC); + ok(closetest_session != NULL,"InternetOpen failed with error %u\n", GetLastError()); + + pInternetSetStatusCallbackA(closetest_session, closetest_callback); + + closetest_conn = InternetConnectA(closetest_session, "source.winehq.org", INTERNET_INVALID_PORT_NUMBER, + NULL, NULL, INTERNET_SERVICE_HTTP, 0x0, 0xdeadbeef); + ok(closetest_conn != NULL,"InternetConnect failed with error %u\n", GetLastError()); + + closetest_req = HttpOpenRequestA(closetest_conn, "GET", "winegecko.php", NULL, NULL, NULL, + INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, 0xdeadbead); + + res = HttpSendRequestA(closetest_req, NULL, -1, NULL, 0); + ok(!res && (GetLastError() == ERROR_IO_PENDING), + "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); + + len = sizeof(flags); + res = InternetQueryOptionA(closetest_req, INTERNET_OPTION_REQUEST_FLAGS, &flags, &len); + ok(res, "InternetQueryOptionA(%p INTERNET_OPTION_URL) failed: %u\n", closetest_req, GetLastError()); + + res = InternetCloseHandle(closetest_session); + ok(res, "InternetCloseHandle failed: %u\n", GetLastError()); + closetest_closed = TRUE; + trace("Closed session handle\n"); + + res = InternetCloseHandle(closetest_conn); + ok(!res && GetLastError() == ERROR_INVALID_HANDLE, "InternetCloseConnection(conn) failed: %x %u\n", + res, GetLastError()); + + res = InternetCloseHandle(closetest_req); + ok(!res && GetLastError() == ERROR_INVALID_HANDLE, "InternetCloseConnection(req) failed: %x %u\n", + res, GetLastError()); + + len = sizeof(flags); + res = InternetQueryOptionA(closetest_req, INTERNET_OPTION_REQUEST_FLAGS, &flags, &len); + ok(!res && GetLastError() == ERROR_INVALID_HANDLE, + "InternetQueryOptionA(%p INTERNET_OPTION_URL) failed: %x %u, expected TRUE ERROR_INVALID_HANDLE\n", + closetest_req, res, GetLastError()); +} + static void init_status_tests(void) { memset(expect, 0, sizeof(expect)); @@ -2946,6 +3435,8 @@ static void init_status_tests(void) memset(wine_allow, 0, sizeof(wine_allow)); memset(notified, 0, sizeof(notified)); memset(status_string, 0, sizeof(status_string)); + +#define STATUS_STRING(status) status_string[status] = #status STATUS_STRING(INTERNET_STATUS_RESOLVING_NAME); STATUS_STRING(INTERNET_STATUS_NAME_RESOLVED); STATUS_STRING(INTERNET_STATUS_CONNECTING_TO_SERVER); @@ -2972,36 +3463,41 @@ static void init_status_tests(void) STATUS_STRING(INTERNET_STATUS_P3P_HEADER); STATUS_STRING(INTERNET_STATUS_P3P_POLICYREF); STATUS_STRING(INTERNET_STATUS_COOKIE_HISTORY); -} #undef STATUS_STRING +} START_TEST(http) { HMODULE hdll; hdll = GetModuleHandleA("wininet.dll"); + + if(!GetProcAddress(hdll, "InternetGetCookieExW")) { + win_skip("Too old IE (older than 6.0)\n"); + return; + } + pInternetSetStatusCallbackA = (void*)GetProcAddress(hdll, "InternetSetStatusCallbackA"); - if (!pInternetSetStatusCallbackA) - skip("skipping the InternetReadFile tests\n"); - else - { - init_status_tests(); - InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[0]); - InternetReadFile_test(0, &test_data[0]); - first_connection_to_test_url = TRUE; - InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[1]); - InternetReadFile_test(0, &test_data[1]); - InternetReadFileExA_test(INTERNET_FLAG_ASYNC); - test_open_url_async(); - test_async_HttpSendRequestEx(); - } + init_status_tests(); + test_InternetCloseHandle(); + InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[0]); + InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[1]); + InternetReadFile_test(0, &test_data[1]); + first_connection_to_test_url = TRUE; + InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[2]); + InternetReadFile_test(0, &test_data[2]); + InternetReadFileExA_test(INTERNET_FLAG_ASYNC); + test_open_url_async(); + test_async_HttpSendRequestEx(); InternetOpenRequest_test(); test_http_cache(); InternetOpenUrlA_test(); HttpHeaders_test(); test_http_connection(); + test_secure_connection(); test_user_agent_header(); test_bogus_accept_types_array(); InternetReadFile_chunked_test(); HttpSendRequestEx_test(); + InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[3]); } diff --git a/rostests/winetests/wininet/internet.c b/rostests/winetests/wininet/internet.c index 4da9c218839..0b5f6eabd88 100644 --- a/rostests/winetests/wininet/internet.c +++ b/rostests/winetests/wininet/internet.c @@ -22,6 +22,7 @@ #include #include "windef.h" #include "winbase.h" +#include "winuser.h" #include "wininet.h" #include "winerror.h" #include "winreg.h" @@ -103,7 +104,7 @@ static void test_InternetCanonicalizeUrlA(void) res = InternetCanonicalizeUrlA("file:///C:/Program%20Files/Atmel/AVR%20Tools/STK500/STK500.xml", buffer, &dwSize, ICU_DECODE | ICU_NO_ENCODE); ok(res, "InternetCanonicalizeUrlA failed %u\n", GetLastError()); ok(dwSize == lstrlenA(buffer), "got %d expected %d\n", dwSize, lstrlenA(buffer)); - todo_wine ok(!lstrcmpA("file://C:\\Program Files\\Atmel\\AVR Tools\\STK500\\STK500.xml", buffer), + ok(!lstrcmpA("file://C:\\Program Files\\Atmel\\AVR Tools\\STK500\\STK500.xml", buffer), "got %s expected 'file://C:\\Program Files\\Atmel\\AVR Tools\\STK500\\STK500.xml'\n", buffer); /* buffer is larger as the required size */ @@ -163,7 +164,7 @@ static void test_InternetCanonicalizeUrlA(void) static void test_InternetQueryOptionA(void) { HINTERNET hinet,hurl; - DWORD len; + DWORD len, val; DWORD err; static const char useragent[] = {"Wininet Test"}; char *buffer; @@ -247,6 +248,19 @@ static void test_InternetQueryOptionA(void) ok(err == ERROR_INSUFFICIENT_BUFFER, "Got wrong error code%d\n",err); InternetCloseHandle(hinet); + + len = sizeof(val); + retval = InternetQueryOptionA(NULL, INTERNET_OPTION_MAX_CONNS_PER_SERVER, &val, &len); + ok(retval == TRUE,"Got wrong return value %d\n", retval); + ok(len == sizeof(val), "got %d\n", len); + ok(val == 2, "got %d\n", val); + + len = sizeof(val); + retval = InternetQueryOptionA(NULL, INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER, &val, &len); + ok(retval == TRUE,"Got wrong return value %d\n", retval); + ok(len == sizeof(val), "got %d\n", len); + ok(val == 4, "got %d\n", val); + } static void test_get_cookie(void) @@ -290,6 +304,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -301,6 +316,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com/foobar", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -312,6 +328,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com/foobar/", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -323,6 +340,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com/foo/bar", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -334,6 +352,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com/barfoo", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -345,6 +364,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com/barfoo/", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -356,6 +376,7 @@ static void test_complicated_cookie(void) len = 1024; ret = InternetGetCookie("http://testing.example.com/bar/foo", NULL, buffer, &len); + ok(ret == TRUE,"InternetGetCookie failed\n"); ok(strstr(buffer,"A=B")!=NULL,"A=B missing\n"); ok(strstr(buffer,"C=D")!=NULL,"C=D missing\n"); ok(strstr(buffer,"E=F")!=NULL,"E=F missing\n"); @@ -1123,12 +1144,164 @@ static void test_Option_PerConnectionOptionA(void) HeapFree(GetProcessHeap(), 0, list.pOptions); } +#define FLAG_TODO 0x1 +#define FLAG_NEEDREQ 0x2 +#define FLAG_UNIMPL 0x4 + +static void test_InternetErrorDlg(void) +{ + HINTERNET ses, con, req; + DWORD res, flags; + HWND hwnd; + ULONG i; + static const struct { + DWORD error; + DWORD res; + DWORD test_flags; + } no_ui_res[] = { + { ERROR_INTERNET_INCORRECT_PASSWORD , ERROR_SUCCESS, FLAG_NEEDREQ }, + { ERROR_INTERNET_SEC_CERT_DATE_INVALID , ERROR_CANCELLED, 0 }, + { ERROR_INTERNET_SEC_CERT_CN_INVALID , ERROR_CANCELLED, 0 }, + { ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR , ERROR_SUCCESS, 0 }, + { ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR , ERROR_SUCCESS, FLAG_TODO }, + { ERROR_INTERNET_MIXED_SECURITY , ERROR_CANCELLED, FLAG_TODO }, + { ERROR_INTERNET_CHG_POST_IS_NON_SECURE , ERROR_CANCELLED, FLAG_TODO }, + { ERROR_INTERNET_POST_IS_NON_SECURE , ERROR_SUCCESS, 0 }, + { ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED, ERROR_CANCELLED, FLAG_NEEDREQ|FLAG_TODO }, + { ERROR_INTERNET_INVALID_CA , ERROR_CANCELLED, 0 }, + { ERROR_INTERNET_HTTPS_HTTP_SUBMIT_REDIR, ERROR_CANCELLED, FLAG_TODO }, + { ERROR_INTERNET_INSERT_CDROM , ERROR_CANCELLED, FLAG_TODO|FLAG_NEEDREQ|FLAG_UNIMPL }, + { ERROR_INTERNET_SEC_CERT_ERRORS , ERROR_CANCELLED, 0 }, + { ERROR_INTERNET_SEC_CERT_REV_FAILED , ERROR_CANCELLED, FLAG_TODO }, + { ERROR_HTTP_COOKIE_NEEDS_CONFIRMATION , ERROR_HTTP_COOKIE_DECLINED, FLAG_TODO }, + { ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT , ERROR_CANCELLED, FLAG_TODO }, + { ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT, ERROR_CANCELLED, FLAG_TODO }, + { ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION, ERROR_CANCELLED, FLAG_TODO }, + { ERROR_INTERNET_SEC_CERT_REVOKED , ERROR_CANCELLED, 0 }, + { 0, ERROR_NOT_SUPPORTED } + }; + + flags = 0; + + res = InternetErrorDlg(NULL, NULL, 12055, flags, NULL); + ok(res == ERROR_INVALID_HANDLE, "Got %d\n", res); + + ses = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + ok(ses != 0, "InternetOpen failed: 0x%08x\n", GetLastError()); + con = InternetConnect(ses, "www.winehq.org", 80, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(con != 0, "InternetConnect failed: 0x%08x\n", GetLastError()); + req = HttpOpenRequest(con, "GET", "/", NULL, NULL, NULL, 0, 0); + ok(req != 0, "HttpOpenRequest failed: 0x%08x\n", GetLastError()); + + /* NULL hwnd and FLAGS_ERROR_UI_FLAGS_NO_UI not set */ + for(i = INTERNET_ERROR_BASE; i < INTERNET_ERROR_LAST; i++) + { + res = InternetErrorDlg(NULL, req, i, flags, NULL); + ok(res == ERROR_INVALID_HANDLE, "Got %d (%d)\n", res, i); + } + + hwnd = GetDesktopWindow(); + ok(hwnd != NULL, "GetDesktopWindow failed (%d)\n", GetLastError()); + + flags = FLAGS_ERROR_UI_FLAGS_NO_UI; + for(i = INTERNET_ERROR_BASE; i < INTERNET_ERROR_LAST; i++) + { + DWORD expected, test_flags, j; + + for(j = 0; no_ui_res[j].error != 0; ++j) + if(no_ui_res[j].error == i) + break; + + test_flags = no_ui_res[j].test_flags; + expected = no_ui_res[j].res; + + /* Try an invalid request handle */ + res = InternetErrorDlg(hwnd, (HANDLE)0xdeadbeef, i, flags, NULL); + if(res == ERROR_CALL_NOT_IMPLEMENTED) + { + todo_wine ok(test_flags & FLAG_UNIMPL, "%i is unexpectedly unimplemented.\n", i); + continue; + } + else + todo_wine ok(res == ERROR_INVALID_HANDLE, "Got %d (%d)\n", res, i); + + /* With a valid req */ + if(i == ERROR_INTERNET_NEED_UI) + continue; /* Crashes on windows XP */ + + if(i == ERROR_INTERNET_SEC_CERT_REVOKED) + continue; /* Interactive (XP, Win7) */ + + res = InternetErrorDlg(hwnd, req, i, flags, NULL); + + /* Handle some special cases */ + switch(i) + { + case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR: + case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR: + if(res == ERROR_CANCELLED) + { + /* Some windows XP, w2k3 x64, W2K8 */ + win_skip("Skipping some tests for %d\n", i); + continue; + } + break; + case ERROR_INTERNET_FORTEZZA_LOGIN_NEEDED: + if(res != expected) + { + /* Windows XP, W2K3 */ + ok(res == NTE_PROV_TYPE_NOT_DEF, "Got %d\n", res); + win_skip("Skipping some tests for %d\n", i); + continue; + } + break; + default: break; + } + + if(test_flags & FLAG_TODO) + todo_wine ok(res == expected, "Got %d, expected %d (%d)\n", res, expected, i); + else + ok(res == expected, "Got %d, expected %d (%d)\n", res, expected, i); + + /* Same thing with NULL hwnd */ + res = InternetErrorDlg(NULL, req, i, flags, NULL); + if(test_flags & FLAG_TODO) + todo_wine ok(res == expected, "Got %d, expected %d (%d)\n", res, expected, i); + else + ok(res == expected, "Got %d, expected %d (%d)\n", res, expected, i); + + + /* With a null req */ + if(test_flags & FLAG_NEEDREQ) + expected = ERROR_INVALID_PARAMETER; + + res = InternetErrorDlg(hwnd, NULL, i, flags, NULL); + if( test_flags & FLAG_TODO || i == ERROR_INTERNET_INCORRECT_PASSWORD) + todo_wine ok(res == expected, "Got %d, expected %d (%d)\n", res, expected, i); + else + ok(res == expected, "Got %d, expected %d (%d)\n", res, expected, i); + } + + res = InternetCloseHandle(req); + ok(res == TRUE, "InternetCloseHandle failed: 0x%08x\n", GetLastError()); + res = InternetCloseHandle(con); + ok(res == TRUE, "InternetCloseHandle failed: 0x%08x\n", GetLastError()); + res = InternetCloseHandle(ses); + ok(res == TRUE, "InternetCloseHandle failed: 0x%08x\n", GetLastError()); +} + /* ############################### */ START_TEST(internet) { HMODULE hdll; hdll = GetModuleHandleA("wininet.dll"); + + if(!GetProcAddress(hdll, "InternetGetCookieExW")) { + win_skip("Too old IE (older than 6.0)\n"); + return; + } + pCreateUrlCacheContainerA = (void*)GetProcAddress(hdll, "CreateUrlCacheContainerA"); pCreateUrlCacheContainerW = (void*)GetProcAddress(hdll, "CreateUrlCacheContainerW"); pInternetTimeFromSystemTimeA = (void*)GetProcAddress(hdll, "InternetTimeFromSystemTimeA"); @@ -1147,6 +1320,7 @@ START_TEST(internet) test_null(); test_Option_PerConnectionOption(); test_Option_PerConnectionOptionA(); + test_InternetErrorDlg(); if (!pInternetTimeFromSystemTimeA) win_skip("skipping the InternetTime tests\n"); diff --git a/rostests/winetests/wininet/url.c b/rostests/winetests/wininet/url.c index 6dc1369e0b8..15fbb09759e 100644 --- a/rostests/winetests/wininet/url.c +++ b/rostests/winetests/wininet/url.c @@ -106,30 +106,76 @@ typedef struct { int path_len; int extra_off; int extra_len; + const char *exp_scheme; + const char *exp_hostname; + const char *exp_username; + const char *exp_password; + const char *exp_urlpath; + const char *exp_extrainfo; } crack_url_test_t; static const crack_url_test_t crack_url_tests[] = { {"http://www.winehq.org/site/about#hi", - 0, 4, INTERNET_SCHEME_HTTP, 7, 14, -1, 80, -1, 0, -1, 0, 21, 11, 32, 3}, + 0, 4, INTERNET_SCHEME_HTTP, 7, 14, -1, 80, -1, 0, -1, 0, 21, 11, 32, 3, + "http", "www.winehq.org", "", "", "/site/about", "#hi"}, {"http://www.myserver.com/myscript.php?arg=1", - 0, 4, INTERNET_SCHEME_HTTP, 7, 16, -1, 80, -1, 0, -1, 0, 23, 13, 36, 6}, + 0, 4, INTERNET_SCHEME_HTTP, 7, 16, -1, 80, -1, 0, -1, 0, 23, 13, 36, 6, + "http", "www.myserver.com", "", "", "/myscript.php", "?arg=1"}, {"http://www.winehq.org?test=123", - 0, 4, INTERNET_SCHEME_HTTP, 7, 14, 23, 80, -1, 0, -1, 0, 21, 0, 21, 9}, + 0, 4, INTERNET_SCHEME_HTTP, 7, 14, 23, 80, -1, 0, -1, 0, 21, 0, 21, 9, + "http", "www.winehq.org", "", "", "", "?test=123"}, {"file:///C:/Program%20Files/Atmel/AVR%20Tools/STK500/STK500.xml", - 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 55, -1, 0}, + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 55, -1, 0, + "file", "", "", "", "C:\\Program Files\\Atmel\\AVR Tools\\STK500\\STK500.xml", ""}, {"fide:///C:/Program%20Files/Atmel/AVR%20Tools/STK500/STK500.xml", - 0, 4, INTERNET_SCHEME_UNKNOWN, 7, 0, -1, 0, -1, 0, -1, 0, 7, 55, -1, 0}, + 0, 4, INTERNET_SCHEME_UNKNOWN, 7, 0, -1, 0, -1, 0, -1, 0, 7, 55, -1, 0, + "fide", "", "", "", "/C:/Program%20Files/Atmel/AVR%20Tools/STK500/STK500.xml", ""}, {"file://C:/Program%20Files/Atmel/AVR%20Tools/STK500/STK500.xml", - 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 54, -1, 0}, + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 54, -1, 0, + "file", "", "", "", "C:\\Program%20Files\\Atmel\\AVR%20Tools\\STK500\\STK500.xml", ""}, + {"file://C:/Program%20Files/Atmel/..", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 27, -1, 0, + "file", "", "", "", "C:\\Program%20Files\\Atmel\\..\\", ""}, + {"file://C:/Program%20Files/Atmel/../Asdf.xml", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 36, -1, 0, + "file", "", "", "", "C:\\Program%20Files\\Atmel\\..\\Asdf.xml", ""}, + {"file:///C:/Program%20Files/Atmel/..", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 28, -1, 0, + "file", "", "", "", "C:\\Program Files\\Atmel\\..\\", ""}, + {"file:///C:/Program%20Files/Atmel/../Asdf.xml", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 37, -1, 0, + "file", "", "", "", "C:\\Program Files\\Atmel\\..\\Asdf.xml", ""}, + {"file://C:/Program%20Files/Atmel/.", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 26, -1, 0, + "file", "", "", "", "C:\\Program%20Files\\Atmel\\.\\", ""}, + {"file://C:/Program%20Files/Atmel/./Asdf.xml", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 35, -1, 0, + "file", "", "", "", "C:\\Program%20Files\\Atmel\\.\\Asdf.xml", ""}, + {"file:///C:/Program%20Files/Atmel/.", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 27, -1, 0, + "file", "", "", "", "C:\\Program Files\\Atmel\\.\\", ""}, + {"file:///C:/Program%20Files/Atmel/./Asdf.xml", + 0, 4, INTERNET_SCHEME_FILE, -1, 0, -1, 0, -1, 0, -1, 0, 7, 36, -1, 0, + "file", "", "", "", "C:\\Program Files\\Atmel\\.\\Asdf.xml", ""}, }; +static const WCHAR *w_str_of(const char *str) +{ + static WCHAR buf[512]; + MultiByteToWideChar(CP_ACP, 0, str, -1, buf, sizeof(buf)/sizeof(buf[0])); + return buf; +} + static void test_crack_url(const crack_url_test_t *test) { WCHAR buf[INTERNET_MAX_URL_LENGTH]; URL_COMPONENTSW urlw; URL_COMPONENTSA url; + char scheme[32], hostname[1024], username[1024]; + char password[1024], extrainfo[1024], urlpath[1024]; BOOL b; + /* test InternetCrackUrlA with NULL buffers */ zero_compsA(&url, 1, 1, 1, 1, 1, 1); b = InternetCrackUrlA(test->url, strlen(test->url), 0, &url); @@ -191,6 +237,7 @@ static void test_crack_url(const crack_url_test_t *test) ok(url.dwExtraInfoLength == test->extra_len, "[%s] url.lpszExtraInfoLength = %d, expected %d\n", test->url, url.dwExtraInfoLength, test->extra_len); + /* test InternetCrackUrlW with NULL buffers */ memset(&urlw, 0, sizeof(URL_COMPONENTSW)); urlw.dwStructSize = sizeof(URL_COMPONENTSW); urlw.dwSchemeLength = 1; @@ -271,6 +318,116 @@ static void test_crack_url(const crack_url_test_t *test) ok(urlw.dwExtraInfoLength == test->extra_len, "[%s] urlw.lpszExtraInfoLength = %d, expected %d\n", test->url, urlw.dwExtraInfoLength, test->extra_len); } + + /* test InternetCrackUrlA with valid buffers */ + memset(&url, 0, sizeof(URL_COMPONENTSA)); + url.dwStructSize = sizeof(URL_COMPONENTSA); + url.lpszScheme = scheme; + url.dwSchemeLength = sizeof(scheme); + url.lpszHostName = hostname; + url.dwHostNameLength = sizeof(hostname); + url.lpszUserName = username; + url.dwUserNameLength = sizeof(username); + url.lpszPassword = password; + url.dwPasswordLength = sizeof(password); + url.lpszUrlPath = urlpath; + url.dwUrlPathLength = sizeof(urlpath); + url.lpszExtraInfo = extrainfo; + url.dwExtraInfoLength = sizeof(extrainfo); + + b = InternetCrackUrlA(test->url, strlen(test->url), 0, &url); + ok(b, "InternetCrackUrlA failed with error %d\n", GetLastError()); + + ok(url.dwSchemeLength == strlen(test->exp_scheme), "[%s] Got wrong scheme length: %d\n", + test->url, url.dwSchemeLength); + ok(!strcmp(scheme, test->exp_scheme), "[%s] Got wrong scheme, expected: %s, got: %s\n", + test->url, test->exp_scheme, scheme); + + ok(url.nScheme == test->scheme, "[%s] Got wrong nScheme, expected: %d, got: %d\n", + test->url, test->scheme, url.nScheme); + + ok(url.dwHostNameLength == strlen(test->exp_hostname), "[%s] Got wrong hostname length: %d\n", + test->url, url.dwHostNameLength); + ok(!strcmp(hostname, test->exp_hostname), "[%s] Got wrong hostname, expected: %s, got: %s\n", + test->url, test->exp_hostname, hostname); + + ok(url.nPort == test->port, "[%s] Got wrong port, expected: %d, got: %d\n", + test->url, test->port, url.nPort); + + ok(url.dwUserNameLength == strlen(test->exp_username), "[%s] Got wrong username length: %d\n", + test->url, url.dwUserNameLength); + ok(!strcmp(username, test->exp_username), "[%s] Got wrong username, expected: %s, got: %s\n", + test->url, test->exp_username, username); + + ok(url.dwPasswordLength == strlen(test->exp_password), "[%s] Got wrong password length: %d\n", + test->url, url.dwPasswordLength); + ok(!strcmp(password, test->exp_password), "[%s] Got wrong password, expected: %s, got: %s\n", + test->url, test->exp_password, password); + + ok(url.dwUrlPathLength == strlen(test->exp_urlpath), "[%s] Got wrong urlpath length: %d\n", + test->url, url.dwUrlPathLength); + ok(!strcmp(urlpath, test->exp_urlpath), "[%s] Got wrong urlpath, expected: %s, got: %s\n", + test->url, test->exp_urlpath, urlpath); + + ok(url.dwExtraInfoLength == strlen(test->exp_extrainfo), "[%s] Got wrong extrainfo length: %d\n", + test->url, url.dwExtraInfoLength); + ok(!strcmp(extrainfo, test->exp_extrainfo), "[%s] Got wrong extrainfo, expected: %s, got: %s\n", + test->url, test->exp_extrainfo, extrainfo); + + /* test InternetCrackUrlW with valid buffers */ + memset(&urlw, 0, sizeof(URL_COMPONENTSW)); + urlw.dwStructSize = sizeof(URL_COMPONENTSW); + urlw.lpszScheme = (WCHAR*)scheme; + urlw.dwSchemeLength = sizeof(scheme) / sizeof(WCHAR); + urlw.lpszHostName = (WCHAR*)hostname; + urlw.dwHostNameLength = sizeof(hostname) / sizeof(WCHAR); + urlw.lpszUserName = (WCHAR*)username; + urlw.dwUserNameLength = sizeof(username) / sizeof(WCHAR); + urlw.lpszPassword = (WCHAR*)password; + urlw.dwPasswordLength = sizeof(password) / sizeof(WCHAR); + urlw.lpszUrlPath = (WCHAR*)urlpath; + urlw.dwUrlPathLength = sizeof(urlpath) / sizeof(WCHAR); + urlw.lpszExtraInfo = (WCHAR*)extrainfo; + urlw.dwExtraInfoLength = sizeof(extrainfo) / sizeof(WCHAR); + + b = InternetCrackUrlW(buf, lstrlenW(buf), 0, &urlw); + ok(b, "InternetCrackUrlW failed with error %d\n", GetLastError()); + + ok(urlw.dwSchemeLength == strlen(test->exp_scheme), "[%s] Got wrong scheme length: %d\n", + test->url, urlw.dwSchemeLength); + ok(!lstrcmpW((WCHAR*)scheme, w_str_of(test->exp_scheme)), "[%s] Got wrong scheme, expected: %s, got: %s\n", + test->url, test->exp_scheme, wine_dbgstr_w((WCHAR*)scheme)); + + ok(urlw.nScheme == test->scheme, "[%s] Got wrong nScheme, expected: %d, got: %d\n", + test->url, test->scheme, urlw.nScheme); + + ok(urlw.dwHostNameLength == strlen(test->exp_hostname), "[%s] Got wrong hostname length: %d\n", + test->url, urlw.dwHostNameLength); + ok(!lstrcmpW((WCHAR*)hostname, w_str_of(test->exp_hostname)), "[%s] Got wrong hostname, expected: %s, got: %s\n", + test->url, test->exp_hostname, wine_dbgstr_w((WCHAR*)hostname)); + + ok(urlw.nPort == test->port, "[%s] Got wrong port, expected: %d, got: %d\n", + test->url, test->port, urlw.nPort); + + ok(urlw.dwUserNameLength == strlen(test->exp_username), "[%s] Got wrong username length: %d\n", + test->url, urlw.dwUserNameLength); + ok(!lstrcmpW((WCHAR*)username, w_str_of(test->exp_username)), "[%s] Got wrong username, expected: %s, got: %s\n", + test->url, test->exp_username, wine_dbgstr_w((WCHAR*)username)); + + ok(urlw.dwPasswordLength == strlen(test->exp_password), "[%s] Got wrong password length: %d\n", + test->url, urlw.dwPasswordLength); + ok(!lstrcmpW((WCHAR*)password, w_str_of(test->exp_password)), "[%s] Got wrong password, expected: %s, got: %s\n", + test->url, test->exp_password, wine_dbgstr_w((WCHAR*)password)); + + ok(urlw.dwUrlPathLength == strlen(test->exp_urlpath), "[%s] Got wrong urlpath length: %d\n", + test->url, urlw.dwUrlPathLength); + ok(!lstrcmpW((WCHAR*)urlpath, w_str_of(test->exp_urlpath)), "[%s] Got wrong urlpath, expected: %s, got: %s\n", + test->url, test->exp_urlpath, wine_dbgstr_w((WCHAR*)urlpath)); + + ok(urlw.dwExtraInfoLength == strlen(test->exp_extrainfo), "[%s] Got wrong extrainfo length: %d\n", + test->url, urlw.dwExtraInfoLength); + ok(!lstrcmpW((WCHAR*)extrainfo, w_str_of(test->exp_extrainfo)), "[%s] Got wrong extrainfo, expected: %s, got: %s\n", + test->url, test->exp_extrainfo, wine_dbgstr_w((WCHAR*)extrainfo)); } static void InternetCrackUrl_test(void) @@ -308,21 +465,21 @@ static void InternetCrackUrl_test(void) ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); GLE = GetLastError(); ok(ret==firstret && (GLE==firstGLE), "InternetCrackUrl returned %d with GLE=%d (expected to return %d)\n", - ret, GetLastError(), firstret); + ret, GLE, firstret); copy_compsA(&urlSrc, &urlComponents, 32, 1024, 0, 1024, 2048, 1024); SetLastError(0xdeadbeef); ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); GLE = GetLastError(); ok(ret==firstret && (GLE==firstGLE), "InternetCrackUrl returned %d with GLE=%d (expected to return %d)\n", - ret, GetLastError(), firstret); + ret, GLE, firstret); copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 0, 2048, 1024); SetLastError(0xdeadbeef); ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); GLE = GetLastError(); ok(ret==firstret && (GLE==firstGLE), "InternetCrackUrl returned %d with GLE=%d (expected to return %d)\n", - ret, GetLastError(), firstret); + ret, GLE, firstret); copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 0, 1024); SetLastError(0xdeadbeef); @@ -946,6 +1103,11 @@ START_TEST(url) { int i; + if(!GetProcAddress(GetModuleHandleA("wininet.dll"), "InternetGetCookieExW")) { + win_skip("Too old IE (older than 6.0)\n"); + return; + } + for(i=0; i < sizeof(crack_url_tests)/sizeof(*crack_url_tests); i++) test_crack_url(crack_url_tests+i); diff --git a/rostests/winetests/wininet/urlcache.c b/rostests/winetests/wininet/urlcache.c index 65e433c69de..5c12a81a4bd 100644 --- a/rostests/winetests/wininet/urlcache.c +++ b/rostests/winetests/wininet/urlcache.c @@ -96,7 +96,7 @@ static void test_find_url_cache_entriesA(void) static void test_GetUrlCacheEntryInfoExA(void) { BOOL ret; - DWORD cbCacheEntryInfo; + DWORD cbCacheEntryInfo, cbRedirectUrl; LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo; SetLastError(0xdeadbeef); @@ -142,6 +142,16 @@ static void test_GetUrlCacheEntryInfoExA(void) ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetUrlCacheEntryInfoEx should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError()); HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); + + /* Querying the redirect URL fails with ERROR_INVALID_PARAMETER */ + SetLastError(0xdeadbeef); + ret = GetUrlCacheEntryInfoEx(TEST_URL, NULL, NULL, NULL, &cbRedirectUrl, NULL, 0); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = GetUrlCacheEntryInfoEx(TEST_URL, NULL, &cbCacheEntryInfo, NULL, &cbRedirectUrl, NULL, 0); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); } static void test_RetrieveUrlCacheEntryA(void) @@ -171,15 +181,172 @@ static void test_RetrieveUrlCacheEntryA(void) ok(GetLastError() == ERROR_INVALID_PARAMETER, "RetrieveUrlCacheEntryFile should have set last error to ERROR_INVALID_PARAMETER instead of %d\n", GetLastError()); } +static void test_IsUrlCacheEntryExpiredA(void) +{ + static const char uncached_url[] = + "What's the airspeed velocity of an unladen swallow?"; + BOOL ret; + FILETIME ft; + DWORD size; + LPINTERNET_CACHE_ENTRY_INFO info; + ULARGE_INTEGER exp_time; + + /* The function returns TRUE when the output time is NULL or the tested URL + * is NULL. + */ + ret = IsUrlCacheEntryExpiredA(NULL, 0, NULL); + ok(ret, "expected TRUE\n"); + ft.dwLowDateTime = 0xdeadbeef; + ft.dwHighDateTime = 0xbaadf00d; + ret = IsUrlCacheEntryExpiredA(NULL, 0, &ft); + ok(ret, "expected TRUE\n"); + ok(ft.dwLowDateTime == 0xdeadbeef && ft.dwHighDateTime == 0xbaadf00d, + "expected time to be unchanged, got (%u,%u)\n", + ft.dwLowDateTime, ft.dwHighDateTime); + ret = IsUrlCacheEntryExpiredA(TEST_URL, 0, NULL); + ok(ret, "expected TRUE\n"); + + /* The return value should indicate whether the URL is expired, + * and the filetime indicates the last modified time, but a cache entry + * with a zero expire time is "not expired". + */ + ft.dwLowDateTime = 0xdeadbeef; + ft.dwHighDateTime = 0xbaadf00d; + ret = IsUrlCacheEntryExpiredA(TEST_URL, 0, &ft); + ok(!ret, "expected FALSE\n"); + ok(!ft.dwLowDateTime && !ft.dwHighDateTime, + "expected time (0,0), got (%u,%u)\n", + ft.dwLowDateTime, ft.dwHighDateTime); + + /* Same behavior with bogus flags. */ + ft.dwLowDateTime = 0xdeadbeef; + ft.dwHighDateTime = 0xbaadf00d; + ret = IsUrlCacheEntryExpiredA(TEST_URL, 0xffffffff, &ft); + ok(!ret, "expected FALSE\n"); + ok(!ft.dwLowDateTime && !ft.dwHighDateTime, + "expected time (0,0), got (%u,%u)\n", + ft.dwLowDateTime, ft.dwHighDateTime); + + /* Set the expire time to a point in the past.. */ + ret = GetUrlCacheEntryInfo(TEST_URL, NULL, &size); + ok(!ret, "GetUrlCacheEntryInfo should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + info = HeapAlloc(GetProcessHeap(), 0, size); + ret = GetUrlCacheEntryInfo(TEST_URL, info, &size); + GetSystemTimeAsFileTime(&info->ExpireTime); + exp_time.u.LowPart = info->ExpireTime.dwLowDateTime; + exp_time.u.HighPart = info->ExpireTime.dwHighDateTime; + exp_time.QuadPart -= 10 * 60 * (ULONGLONG)10000000; + info->ExpireTime.dwLowDateTime = exp_time.u.LowPart; + info->ExpireTime.dwHighDateTime = exp_time.u.HighPart; + ret = SetUrlCacheEntryInfo(TEST_URL, info, CACHE_ENTRY_EXPTIME_FC); + ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError()); + ft.dwLowDateTime = 0xdeadbeef; + ft.dwHighDateTime = 0xbaadf00d; + /* and the entry should be expired. */ + ret = IsUrlCacheEntryExpiredA(TEST_URL, 0, &ft); + ok(ret, "expected TRUE\n"); + /* The modified time returned is 0. */ + ok(!ft.dwLowDateTime && !ft.dwHighDateTime, + "expected time (0,0), got (%u,%u)\n", + ft.dwLowDateTime, ft.dwHighDateTime); + /* Set the expire time to a point in the future.. */ + exp_time.QuadPart += 20 * 60 * (ULONGLONG)10000000; + info->ExpireTime.dwLowDateTime = exp_time.u.LowPart; + info->ExpireTime.dwHighDateTime = exp_time.u.HighPart; + ret = SetUrlCacheEntryInfo(TEST_URL, info, CACHE_ENTRY_EXPTIME_FC); + ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError()); + ft.dwLowDateTime = 0xdeadbeef; + ft.dwHighDateTime = 0xbaadf00d; + /* and the entry should no longer be expired. */ + ret = IsUrlCacheEntryExpiredA(TEST_URL, 0, &ft); + ok(!ret, "expected FALSE\n"); + /* The modified time returned is still 0. */ + ok(!ft.dwLowDateTime && !ft.dwHighDateTime, + "expected time (0,0), got (%u,%u)\n", + ft.dwLowDateTime, ft.dwHighDateTime); + /* Set the modified time... */ + GetSystemTimeAsFileTime(&info->LastModifiedTime); + ret = SetUrlCacheEntryInfo(TEST_URL, info, CACHE_ENTRY_MODTIME_FC); + ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError()); + /* and the entry should still be unexpired.. */ + ret = IsUrlCacheEntryExpiredA(TEST_URL, 0, &ft); + ok(!ret, "expected FALSE\n"); + /* but the modified time returned is the last modified time just set. */ + ok(ft.dwLowDateTime == info->LastModifiedTime.dwLowDateTime && + ft.dwHighDateTime == info->LastModifiedTime.dwHighDateTime, + "expected time (%u,%u), got (%u,%u)\n", + info->LastModifiedTime.dwLowDateTime, + info->LastModifiedTime.dwHighDateTime, + ft.dwLowDateTime, ft.dwHighDateTime); + HeapFree(GetProcessHeap(), 0, info); + + /* An uncached URL is implicitly expired, but with unknown time. */ + ft.dwLowDateTime = 0xdeadbeef; + ft.dwHighDateTime = 0xbaadf00d; + ret = IsUrlCacheEntryExpiredA(uncached_url, 0, &ft); + ok(ret, "expected TRUE\n"); + ok(!ft.dwLowDateTime && !ft.dwHighDateTime, + "expected time (0,0), got (%u,%u)\n", + ft.dwLowDateTime, ft.dwHighDateTime); +} + +static void _check_file_exists(LONG l, LPCSTR filename) +{ + HANDLE file; + + file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok_(__FILE__,l)(file != INVALID_HANDLE_VALUE, + "expected file to exist, CreateFile failed with error %d\n", + GetLastError()); + CloseHandle(file); +} + +#define check_file_exists(f) _check_file_exists(__LINE__, f) + +static void _check_file_not_exists(LONG l, LPCSTR filename) +{ + HANDLE file; + + file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + ok_(__FILE__,l)(file == INVALID_HANDLE_VALUE, + "expected file not to exist\n"); + if (file != INVALID_HANDLE_VALUE) + CloseHandle(file); +} + +#define check_file_not_exists(f) _check_file_not_exists(__LINE__, f) + +static void create_and_write_file(LPCSTR filename, void *data, DWORD len) +{ + HANDLE file; + DWORD written; + BOOL ret; + + file = CreateFileA(filename, GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed with error %d\n", GetLastError()); + + ret = WriteFile(file, data, len, &written, NULL); + ok(ret, "WriteFile failed with error %d\n", GetLastError()); + + CloseHandle(file); +} + static void test_urlcacheA(void) { + static char ok_header[] = "HTTP/1.0 200 OK\r\n\r\n"; BOOL ret; HANDLE hFile; - DWORD written; BYTE zero_byte = 0; LPINTERNET_CACHE_ENTRY_INFO lpCacheEntryInfo; DWORD cbCacheEntryInfo; static const FILETIME filetime_zero; + FILETIME now; ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA, 0); ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); @@ -189,20 +356,60 @@ static void test_urlcacheA(void) ok(lstrcmpiA(filenameA, filenameA1), "expected a different file name\n"); - hFile = CreateFileA(filenameA, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA failed with error %d\n", GetLastError()); - - ret = WriteFile(hFile, &zero_byte, sizeof(zero_byte), &written, NULL); - ok(ret, "WriteFile failed with error %d\n", GetLastError()); - - CloseHandle(hFile); + create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte)); ret = CommitUrlCacheEntry(TEST_URL1, NULL, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY, NULL, 0, NULL, NULL); ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + cbCacheEntryInfo = 0; + ret = GetUrlCacheEntryInfo(TEST_URL1, NULL, &cbCacheEntryInfo); + ok(!ret, "GetUrlCacheEntryInfo should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "GetUrlCacheEntryInfo should have set last error to ERROR_INSUFFICIENT_BUFFER instead of %d\n", GetLastError()); + lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo); + ret = GetUrlCacheEntryInfo(TEST_URL1, lpCacheEntryInfo, &cbCacheEntryInfo); + ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError()); + ok(!memcmp(&lpCacheEntryInfo->ExpireTime, &filetime_zero, sizeof(FILETIME)), + "expected zero ExpireTime\n"); + ok(!memcmp(&lpCacheEntryInfo->LastModifiedTime, &filetime_zero, sizeof(FILETIME)), + "expected zero LastModifiedTime\n"); + ok(lpCacheEntryInfo->CacheEntryType == (NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY) || + broken(lpCacheEntryInfo->CacheEntryType == NORMAL_CACHE_ENTRY /* NT4/W2k */), + "expected type NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY, got %08x\n", + lpCacheEntryInfo->CacheEntryType); + ok(!U(*lpCacheEntryInfo).dwExemptDelta, "expected dwExemptDelta 0, got %d\n", + U(*lpCacheEntryInfo).dwExemptDelta); + HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); - ret = CommitUrlCacheEntry(TEST_URL1, NULL, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY, NULL, 0, NULL, NULL); + /* A subsequent commit with a different time/type doesn't change the type */ + GetSystemTimeAsFileTime(&now); + ret = CommitUrlCacheEntry(TEST_URL1, NULL, now, now, NORMAL_CACHE_ENTRY, + (LPBYTE)ok_header, strlen(ok_header), NULL, NULL); ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + cbCacheEntryInfo = 0; + ret = GetUrlCacheEntryInfo(TEST_URL1, NULL, &cbCacheEntryInfo); + ok(!ret, "GetUrlCacheEntryInfo should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo); + ret = GetUrlCacheEntryInfo(TEST_URL1, lpCacheEntryInfo, &cbCacheEntryInfo); + ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError()); + /* but it does change the time.. */ + todo_wine + ok(memcmp(&lpCacheEntryInfo->ExpireTime, &filetime_zero, sizeof(FILETIME)), + "expected positive ExpireTime\n"); + todo_wine + ok(memcmp(&lpCacheEntryInfo->LastModifiedTime, &filetime_zero, sizeof(FILETIME)), + "expected positive LastModifiedTime\n"); + ok(lpCacheEntryInfo->CacheEntryType == (NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY) || + broken(lpCacheEntryInfo->CacheEntryType == NORMAL_CACHE_ENTRY /* NT4/W2k */), + "expected type NORMAL_CACHE_ENTRY|URLHISTORY_CACHE_ENTRY, got %08x\n", + lpCacheEntryInfo->CacheEntryType); + /* and set the headers. */ + todo_wine + ok(lpCacheEntryInfo->dwHeaderInfoSize == 19, + "expected headers size 19, got %d\n", + lpCacheEntryInfo->dwHeaderInfoSize); + HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); ret = CommitUrlCacheEntry(TEST_URL, filenameA, filetime_zero, filetime_zero, NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL); ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); @@ -240,6 +447,7 @@ static void test_urlcacheA(void) test_GetUrlCacheEntryInfoExA(); test_RetrieveUrlCacheEntryA(); + test_IsUrlCacheEntryExpiredA(); if (pDeleteUrlCacheEntryA) { @@ -253,6 +461,255 @@ static void test_urlcacheA(void) ret = DeleteFile(filenameA); todo_wine ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND, "local file should no longer exist\n"); + + /* Creating two entries with the same URL */ + ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA, 0); + ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); + + ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA1, 0); + ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); + + ok(lstrcmpiA(filenameA, filenameA1), "expected a different file name\n"); + + create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte)); + create_and_write_file(filenameA1, &zero_byte, sizeof(zero_byte)); + check_file_exists(filenameA); + check_file_exists(filenameA1); + + ret = CommitUrlCacheEntry(TEST_URL, filenameA, filetime_zero, + filetime_zero, NORMAL_CACHE_ENTRY, (LPBYTE)ok_header, + strlen(ok_header), "html", NULL); + ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + check_file_exists(filenameA); + check_file_exists(filenameA1); + ret = CommitUrlCacheEntry(TEST_URL, filenameA1, filetime_zero, + filetime_zero, COOKIE_CACHE_ENTRY, NULL, 0, "html", NULL); + ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + /* By committing the same URL a second time, the prior entry is + * overwritten... + */ + cbCacheEntryInfo = 0; + SetLastError(0xdeadbeef); + ret = GetUrlCacheEntryInfo(TEST_URL, NULL, &cbCacheEntryInfo); + ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo); + ret = GetUrlCacheEntryInfo(TEST_URL, lpCacheEntryInfo, &cbCacheEntryInfo); + ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError()); + /* with the previous entry type retained.. */ + ok(lpCacheEntryInfo->CacheEntryType & NORMAL_CACHE_ENTRY, + "expected cache entry type NORMAL_CACHE_ENTRY, got %d (0x%08x)\n", + lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType); + /* and the headers overwritten.. */ + todo_wine + ok(!lpCacheEntryInfo->dwHeaderInfoSize, "expected headers size 0, got %d\n", + lpCacheEntryInfo->dwHeaderInfoSize); + HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); + /* and the previous filename shouldn't exist. */ + todo_wine + check_file_not_exists(filenameA); + check_file_exists(filenameA1); + + if (pDeleteUrlCacheEntryA) + { + ret = pDeleteUrlCacheEntryA(TEST_URL); + ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError()); + todo_wine + check_file_not_exists(filenameA); + todo_wine + check_file_not_exists(filenameA1); + /* Just in case, clean up files */ + DeleteFileA(filenameA1); + DeleteFileA(filenameA); + } + + /* Check whether a retrieved cache entry can be deleted before it's + * unlocked: + */ + ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA, 0); + ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); + ret = CommitUrlCacheEntry(TEST_URL, filenameA, filetime_zero, filetime_zero, + NORMAL_CACHE_ENTRY, NULL, 0, "html", NULL); + ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + + cbCacheEntryInfo = 0; + SetLastError(0xdeadbeef); + ret = RetrieveUrlCacheEntryFile(TEST_URL, NULL, &cbCacheEntryInfo, 0); + ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + + lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo); + ret = RetrieveUrlCacheEntryFile(TEST_URL, lpCacheEntryInfo, + &cbCacheEntryInfo, 0); + ok(ret, "RetrieveUrlCacheEntryFile failed with error %d\n", GetLastError()); + + HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); + + if (pDeleteUrlCacheEntryA) + { + ret = pDeleteUrlCacheEntryA(TEST_URL); + todo_wine + ok(!ret, "Expected failure\n"); + todo_wine + ok(GetLastError() == ERROR_SHARING_VIOLATION, + "Expected ERROR_SHARING_VIOLATION, got %d\n", GetLastError()); + check_file_exists(filenameA); + } + if (pUnlockUrlCacheEntryFileA) + { + check_file_exists(filenameA); + ret = pUnlockUrlCacheEntryFileA(TEST_URL, 0); + todo_wine + ok(ret, "UnlockUrlCacheEntryFileA failed: %d\n", GetLastError()); + /* By unlocking the already-deleted cache entry, the file associated + * with it is deleted.. + */ + todo_wine + check_file_not_exists(filenameA); + /* (just in case, delete file) */ + DeleteFileA(filenameA); + } + if (pDeleteUrlCacheEntryA) + { + /* and a subsequent deletion should fail. */ + ret = pDeleteUrlCacheEntryA(TEST_URL); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError()); + } + + /* Test whether preventing a file from being deleted causes + * DeleteUrlCacheEntryA to fail. + */ + ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA, 0); + ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); + + create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte)); + check_file_exists(filenameA); + + ret = CommitUrlCacheEntry(TEST_URL, filenameA, filetime_zero, + filetime_zero, NORMAL_CACHE_ENTRY, (LPBYTE)ok_header, + strlen(ok_header), "html", NULL); + ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + check_file_exists(filenameA); + hFile = CreateFileA(filenameA, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA failed: %d\n", + GetLastError()); + if (pDeleteUrlCacheEntryA) + { + /* DeleteUrlCacheEntryA should succeed.. */ + ret = pDeleteUrlCacheEntryA(TEST_URL); + ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError()); + } + CloseHandle(hFile); + if (pDeleteUrlCacheEntryA) + { + /* and a subsequent deletion should fail.. */ + ret = pDeleteUrlCacheEntryA(TEST_URL); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError()); + } + /* and the file should be untouched. */ + check_file_exists(filenameA); + DeleteFileA(filenameA); + + /* Try creating a sticky entry. Unlike non-sticky entries, the filename + * must have been set already. + */ + SetLastError(0xdeadbeef); + ret = CommitUrlCacheEntry(TEST_URL, NULL, filetime_zero, filetime_zero, + STICKY_CACHE_ENTRY, (LPBYTE)ok_header, strlen(ok_header), "html", + NULL); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = CommitUrlCacheEntry(TEST_URL, NULL, filetime_zero, filetime_zero, + NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY, + (LPBYTE)ok_header, strlen(ok_header), "html", NULL); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA, 0); + ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); + create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte)); + ret = CommitUrlCacheEntry(TEST_URL, filenameA, filetime_zero, filetime_zero, + NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY, + (LPBYTE)ok_header, strlen(ok_header), "html", NULL); + ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + cbCacheEntryInfo = 0; + SetLastError(0xdeadbeef); + ret = GetUrlCacheEntryInfo(TEST_URL, NULL, &cbCacheEntryInfo); + ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo); + ret = GetUrlCacheEntryInfo(TEST_URL, lpCacheEntryInfo, &cbCacheEntryInfo); + ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError()); + ok(lpCacheEntryInfo->CacheEntryType & (NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY), + "expected cache entry type NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY, got %d (0x%08x)\n", + lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType); + ok(U(*lpCacheEntryInfo).dwExemptDelta == 86400, + "expected dwExemptDelta 864000, got %d\n", + U(*lpCacheEntryInfo).dwExemptDelta); + HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); + if (pDeleteUrlCacheEntryA) + { + ret = pDeleteUrlCacheEntryA(TEST_URL); + ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError()); + /* When explicitly deleting the cache entry, the file is also deleted */ + todo_wine + check_file_not_exists(filenameA); + } + /* Test once again, setting the exempt delta via SetUrlCacheEntryInfo */ + ret = CreateUrlCacheEntry(TEST_URL, 0, "html", filenameA, 0); + ok(ret, "CreateUrlCacheEntry failed with error %d\n", GetLastError()); + create_and_write_file(filenameA, &zero_byte, sizeof(zero_byte)); + ret = CommitUrlCacheEntry(TEST_URL, filenameA, filetime_zero, filetime_zero, + NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY, + (LPBYTE)ok_header, strlen(ok_header), "html", NULL); + ok(ret, "CommitUrlCacheEntry failed with error %d\n", GetLastError()); + cbCacheEntryInfo = 0; + SetLastError(0xdeadbeef); + ret = GetUrlCacheEntryInfo(TEST_URL, NULL, &cbCacheEntryInfo); + ok(!ret, "RetrieveUrlCacheEntryFile should have failed\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + lpCacheEntryInfo = HeapAlloc(GetProcessHeap(), 0, cbCacheEntryInfo); + ret = GetUrlCacheEntryInfo(TEST_URL, lpCacheEntryInfo, &cbCacheEntryInfo); + ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError()); + ok(lpCacheEntryInfo->CacheEntryType & (NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY), + "expected cache entry type NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY, got %d (0x%08x)\n", + lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType); + ok(U(*lpCacheEntryInfo).dwExemptDelta == 86400, + "expected dwExemptDelta 864000, got %d\n", + U(*lpCacheEntryInfo).dwExemptDelta); + U(*lpCacheEntryInfo).dwExemptDelta = 0; + ret = SetUrlCacheEntryInfoA(TEST_URL, lpCacheEntryInfo, + CACHE_ENTRY_EXEMPT_DELTA_FC); + ok(ret, "SetUrlCacheEntryInfo failed: %d\n", GetLastError()); + ret = GetUrlCacheEntryInfo(TEST_URL, lpCacheEntryInfo, &cbCacheEntryInfo); + ok(ret, "GetUrlCacheEntryInfo failed with error %d\n", GetLastError()); + ok(!U(*lpCacheEntryInfo).dwExemptDelta, "expected dwExemptDelta 0, got %d\n", + U(*lpCacheEntryInfo).dwExemptDelta); + /* See whether a sticky cache entry has the flag cleared once the exempt + * delta is meaningless. + */ + ok(lpCacheEntryInfo->CacheEntryType & (NORMAL_CACHE_ENTRY|STICKY_CACHE_ENTRY), + "expected cache entry type NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY, got %d (0x%08x)\n", + lpCacheEntryInfo->CacheEntryType, lpCacheEntryInfo->CacheEntryType); + HeapFree(GetProcessHeap(), 0, lpCacheEntryInfo); + if (pDeleteUrlCacheEntryA) + { + ret = pDeleteUrlCacheEntryA(TEST_URL); + ok(ret, "DeleteUrlCacheEntryA failed with error %d\n", GetLastError()); + todo_wine + check_file_not_exists(filenameA); + } } static void test_FindCloseUrlCache(void) @@ -311,6 +768,12 @@ START_TEST(urlcache) { HMODULE hdll; hdll = GetModuleHandleA("wininet.dll"); + + if(!GetProcAddress(hdll, "InternetGetCookieExW")) { + win_skip("Too old IE (older than 6.0)\n"); + return; + } + pDeleteUrlCacheEntryA = (void*)GetProcAddress(hdll, "DeleteUrlCacheEntryA"); pUnlockUrlCacheEntryFileA = (void*)GetProcAddress(hdll, "UnlockUrlCacheEntryFileA"); test_urlcacheA(); diff --git a/rostests/winetests/wininet/wininet.rbuild b/rostests/winetests/wininet/wininet.rbuild index f1ce89a34a3..d2b948f8d05 100644 --- a/rostests/winetests/wininet/wininet.rbuild +++ b/rostests/winetests/wininet/wininet.rbuild @@ -7,6 +7,7 @@ wine wininet ws2_32 + user32 advapi32 ntdll ftp.c