Index: CMakeLists.txt =================================================================== --- modules/rostests/winetests/winhttp/CMakeLists.txt (revision 56828) +++ modules/rostests/winetests/winhttp/CMakeLists.txt (working copy) @@ -1,16 +1,14 @@ -add_definitions( - -D__ROS_LONG64__ - -D_DLL -D__USE_CRTIMP) +add_definitions(-D__ROS_LONG64__) list(APPEND SOURCE notification.c - testlist.c url.c - winhttp.c) + winhttp.c + testlist.c) add_executable(winhttp_winetest ${SOURCE}) -target_link_libraries(winhttp_winetest wine) +target_link_libraries(winhttp_winetest wine uuid) set_module_type(winhttp_winetest win32cui) -add_importlibs(winhttp_winetest winhttp crypt32 ws2_32 advapi32 msvcrt kernel32 ntdll) +add_importlibs(winhttp_winetest winhttp oleaut32 ole32 crypt32 advapi32 ws2_32 msvcrt kernel32 ntdll) add_cd_file(TARGET winhttp_winetest DESTINATION reactos/bin FOR all) Index: notification.c =================================================================== --- modules/rostests/winetests/winhttp/notification.c (revision 56828) +++ modules/rostests/winetests/winhttp/notification.c (working copy) @@ -135,6 +135,16 @@ { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 1 }, { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 1 }, { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0, 1 }, + { winhttp_open_request, WINHTTP_CALLBACK_STATUS_HANDLE_CREATED, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, 0 }, + { winhttp_send_request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, 0 }, + { winhttp_receive_response, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, 0 }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, 0, 1 }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, 0, 1 }, + { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0, 1 }, { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 1, 1 }, { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 1, 1 } }; @@ -190,6 +200,32 @@ setup_test( &info, winhttp_close_handle, __LINE__ ); WinHttpCloseHandle( req ); + + setup_test( &info, winhttp_open_request, __LINE__ ); + req = WinHttpOpenRequest( con, NULL, NULL, NULL, NULL, NULL, 0 ); + ok(req != NULL, "failed to open a request %u\n", GetLastError()); + + ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) ); + ok(ret, "failed to set context value %u\n", GetLastError()); + + setup_test( &info, winhttp_send_request, __LINE__ ); + ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 ); + ok(ret, "failed to send request %u\n", GetLastError()); + + setup_test( &info, winhttp_receive_response, __LINE__ ); + ret = WinHttpReceiveResponse( req, NULL ); + ok(ret, "failed to receive response %u\n", GetLastError()); + + size = sizeof(status); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL ); + ok(ret, "failed unexpectedly %u\n", GetLastError()); + ok(status == 200, "request failed unexpectedly %u\n", status); + + setup_test( &info, winhttp_close_handle, __LINE__ ); + WinHttpCloseHandle( req ); + + setup_test( &info, winhttp_close_handle, __LINE__ ); + WinHttpCloseHandle( req ); WinHttpCloseHandle( con ); WinHttpCloseHandle( ses ); @@ -231,6 +267,29 @@ setup_test( &info, winhttp_close_handle, __LINE__ ); WinHttpCloseHandle( req ); + + setup_test( &info, winhttp_open_request, __LINE__ ); + req = WinHttpOpenRequest( con, NULL, NULL, NULL, NULL, NULL, 0 ); + ok(req != NULL, "failed to open a request %u\n", GetLastError()); + + ret = WinHttpSetOption( req, WINHTTP_OPTION_CONTEXT_VALUE, &context, sizeof(struct info *) ); + ok(ret, "failed to set context value %u\n", GetLastError()); + + setup_test( &info, winhttp_send_request, __LINE__ ); + ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 ); + ok(ret, "failed to send request %u\n", GetLastError()); + + setup_test( &info, winhttp_receive_response, __LINE__ ); + ret = WinHttpReceiveResponse( req, NULL ); + ok(ret, "failed to receive response %u\n", GetLastError()); + + size = sizeof(status); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL ); + ok(ret, "failed unexpectedly %u\n", GetLastError()); + ok(status == 200, "request failed unexpectedly %u\n", status); + + setup_test( &info, winhttp_close_handle, __LINE__ ); + WinHttpCloseHandle( req ); WinHttpCloseHandle( con ); WinHttpCloseHandle( ses ); } Index: testlist.c =================================================================== --- modules/rostests/winetests/winhttp/testlist.c (revision 56828) +++ modules/rostests/winetests/winhttp/testlist.c (working copy) @@ -6,14 +6,14 @@ #define STANDALONE #include "wine/test.h" -extern void func_winhttp(void); extern void func_notification(void); extern void func_url(void); +extern void func_winhttp(void); const struct test winetest_testlist[] = { - { "winhttp", func_winhttp }, - { "notification", func_notification }, - { "url", func_url }, - { 0, 0 } + { "notification", func_notification }, + { "url", func_url }, + { "winhttp", func_winhttp }, + { 0, 0 } }; Index: url.c =================================================================== --- modules/rostests/winetests/winhttp/url.c (revision 56828) +++ modules/rostests/winetests/winhttp/url.c (working copy) @@ -67,9 +67,11 @@ '@','w','w','w','.','w','i','n','e','h','q','.','o','r','g',':','4','4','3','/','s','i','t','e','/','a','b','o','u','t','?','q','u','e','r','y',0}; static const WCHAR url12[] = {'h','t','t','p',':','/','/','e','x','a','m','p','l','e','.','n','e','t','/','p','a','t','h','?','v','a','r','1','=','e','x','a','m','p','l','e','@','e','x','a','m','p','l','e','.','c','o','m','&','v','a','r','2','=','x','&','v','a','r','3','=','y', 0}; +static const WCHAR url13[] = + {'h','t','t','p','s',':','/','/','t','o','o','l','s','.','g','o','o','g','l','e','.','c','o','m','/','s','e','r','v','i','c','e','/','u','p','d','a','t','e','2','?','w','=','3',':','B','x','D','H','o','W','y','8','e','z','M',0}; +static const WCHAR url14[] = + {'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o',' ','g','/','p','a','t','h',' ','w','i','t','h',' ','s','p','a','c','e','s',0}; - - static const WCHAR url_k1[] = {'h','t','t','p',':','/','/','u','s','e','r','n','a','m','e',':','p','a','s','s','w','o','r','d', '@','w','w','w','.','w','i','n','e','h','q','.','o','r','g','/','s','i','t','e','/','a','b','o','u','t',0}; @@ -318,6 +320,10 @@ static void WinHttpCrackUrl_test( void ) { + static const WCHAR hostnameW[] = + {'w','i','n','e','h','q','.','o',' ','g',0}; + static const WCHAR pathW[] = + {'/','p','a','t','h','%','2','0','w','i','t','h','%','2','0','s','p','a','c','e','s',0}; URL_COMPONENTSW uc; WCHAR scheme[20], user[20], pass[20], host[20], path[80], extra[40]; DWORD error; @@ -570,7 +576,6 @@ reset_url_components( &uc ); ret = WinHttpCrackUrl( url12, 0, 0, &uc); - ok( ret, "WinHttpCrackUrl failed\n" ); ok( uc.nScheme == INTERNET_SCHEME_HTTP, "unexpected scheme\n" ); ok( uc.lpszScheme == url12,"unexpected scheme\n" ); @@ -584,6 +589,40 @@ ok( uc.dwUrlPathLength == 5, "unexpected path length\n" ); ok( uc.lpszExtraInfo == url12 + 23, "unexpected extra info\n" ); ok( uc.dwExtraInfoLength == 39, "unexpected extra info length\n" ); + + uc.lpszScheme = scheme; + uc.dwSchemeLength = 20; + uc.lpszHostName = host; + uc.dwHostNameLength = 20; + uc.lpszUserName = NULL; + uc.dwUserNameLength = 0; + uc.lpszPassword = NULL; + uc.dwPasswordLength = 0; + uc.lpszUrlPath = path; + uc.dwUrlPathLength = 40; + uc.lpszExtraInfo = NULL; + uc.dwExtraInfoLength = 0; + uc.nPort = 0; + ret = WinHttpCrackUrl( url13, 0, ICU_DECODE, &uc ); + ok( ret, "WinHttpCrackUrl failed\n" ); + + uc.lpszScheme = scheme; + uc.dwSchemeLength = 20; + uc.lpszHostName = host; + uc.dwHostNameLength = 20; + uc.lpszUserName = NULL; + uc.dwUserNameLength = 0; + uc.lpszPassword = NULL; + uc.dwPasswordLength = 0; + uc.lpszUrlPath = path; + uc.dwUrlPathLength = 40; + uc.lpszExtraInfo = NULL; + uc.dwExtraInfoLength = 0; + uc.nPort = 0; + ret = WinHttpCrackUrl( url14, 0, ICU_ESCAPE|ICU_DECODE, &uc ); + ok( ret, "WinHttpCrackUrl failed\n" ); + ok( !lstrcmpW( uc.lpszHostName, hostnameW ), "unexpected host name\n" ); + ok( !lstrcmpW( uc.lpszUrlPath, pathW ), "unexpected path\n" ); } START_TEST(url) Index: winhttp.c =================================================================== --- modules/rostests/winetests/winhttp/winhttp.c (revision 56828) +++ modules/rostests/winetests/winhttp/winhttp.c (working copy) @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS #include #include #include @@ -26,6 +27,8 @@ #include #include #include +#include "initguid.h" +#include #include "wine/test.h" @@ -656,7 +659,8 @@ test_header_name, buffer, &len, &index); ok(ret == TRUE, "WinHttpQueryHeaders failed: %u\n", GetLastError()); ok(index == 1, "WinHttpQueryHeaders failed to increment index.\n"); - ok(memcmp(buffer, reverse ? test_flag_coalesce_reverse : test_flag_coalesce, sizeof(reverse ? test_flag_coalesce_reverse : test_flag_coalesce)) == 0, "WinHttpQueryHeaders returned incorrect string.\n"); + ok(memcmp(buffer, reverse ? test_flag_coalesce_reverse : test_flag_coalesce, + reverse ? sizeof(test_flag_coalesce_reverse) : sizeof(test_flag_coalesce)) == 0, "WinHttpQueryHeaders returned incorrect string.\n"); len = sizeof(buffer); ret = WinHttpQueryHeaders(request, WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS, @@ -680,7 +684,8 @@ test_header_name, buffer, &len, &index); ok(ret == TRUE, "WinHttpQueryHeaders failed: %u\n", GetLastError()); ok(index == 1, "WinHttpQueryHeaders failed to increment index.\n"); - ok(memcmp(buffer, reverse ? test_flag_coalesce_comma_reverse : test_flag_coalesce_comma, sizeof(reverse ? test_flag_coalesce_comma_reverse : test_flag_coalesce_comma)) == 0, + ok(memcmp(buffer, reverse ? test_flag_coalesce_comma_reverse : test_flag_coalesce_comma, + reverse ? sizeof(test_flag_coalesce_comma_reverse) : sizeof(test_flag_coalesce_comma)) == 0, "WinHttpQueryHeaders returned incorrect string.\n"); len = sizeof(buffer); @@ -706,7 +711,8 @@ test_header_name, buffer, &len, &index); ok(ret == TRUE, "WinHttpQueryHeaders failed: %u\n", GetLastError()); ok(index == 1, "WinHttpQueryHeaders failed to increment index.\n"); - ok(memcmp(buffer, reverse ? test_flag_coalesce_semicolon_reverse : test_flag_coalesce_semicolon, sizeof(reverse ? test_flag_coalesce_semicolon_reverse : test_flag_coalesce_semicolon)) == 0, + ok(memcmp(buffer, reverse ? test_flag_coalesce_semicolon_reverse : test_flag_coalesce_semicolon, + reverse ? sizeof(test_flag_coalesce_semicolon_reverse) : sizeof(test_flag_coalesce_semicolon)) == 0, "WinHttpQueryHeaders returned incorrect string.\n"); len = sizeof(buffer); @@ -1748,11 +1754,6 @@ "Server: winetest\r\n" "\r\n"; -static const char notokmsg[] = -"HTTP/1.1 400 Bad Request\r\n" -"Server: winetest\r\n" -"\r\n"; - static const char noauthmsg[] = "HTTP/1.1 401 Unauthorized\r\n" "Server: winetest\r\n" @@ -1952,6 +1953,7 @@ { static const WCHAR no_headersW[] = {'/','n','o','_','h','e','a','d','e','r','s',0}; HINTERNET ses, con, req; + DWORD error; BOOL ret; ses = WinHttpOpen(test_useragent, 0, NULL, NULL, 0); @@ -1966,14 +1968,54 @@ ret = WinHttpSendRequest(req, NULL, 0, NULL, 0, 0, 0); ok(ret, "failed to send request %u\n", GetLastError()); + SetLastError(0xdeadbeef); ret = WinHttpReceiveResponse(req, NULL); + error = GetLastError(); ok(!ret, "expected failure\n"); + ok(error == ERROR_WINHTTP_INVALID_SERVER_RESPONSE, "got %u\n", error); WinHttpCloseHandle(req); WinHttpCloseHandle(con); WinHttpCloseHandle(ses); } +static void test_bad_header( int port ) +{ + static const WCHAR bad_headerW[] = + {'C','o','n','t','e','n','t','-','T','y','p','e',':',' ', + 't','e','x','t','/','h','t','m','l','\n','\r',0}; + static const WCHAR text_htmlW[] = {'t','e','x','t','/','h','t','m','l',0}; + static const WCHAR content_typeW[] = {'C','o','n','t','e','n','t','-','T','y','p','e',0}; + WCHAR buffer[32]; + HINTERNET ses, con, req; + DWORD index, len; + BOOL ret; + + ses = WinHttpOpen( test_useragent, 0, NULL, NULL, 0 ); + ok( ses != NULL, "failed to open session %u\n", GetLastError() ); + + con = WinHttpConnect( ses, localhostW, port, 0 ); + ok( con != NULL, "failed to open a connection %u\n", GetLastError() ); + + req = WinHttpOpenRequest( con, NULL, NULL, NULL, NULL, NULL, 0 ); + ok( req != NULL, "failed to open a request %u\n", GetLastError() ); + + ret = WinHttpAddRequestHeaders( req, bad_headerW, ~0u, WINHTTP_ADDREQ_FLAG_ADD ); + ok( ret, "failed to add header %u\n", GetLastError() ); + + index = 0; + buffer[0] = 0; + len = sizeof(buffer); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_CUSTOM|WINHTTP_QUERY_FLAG_REQUEST_HEADERS, + content_typeW, buffer, &len, &index ); + ok( ret, "failed to query headers %u\n", GetLastError() ); + ok( !lstrcmpW( buffer, text_htmlW ), "got %s\n", wine_dbgstr_w(buffer) ); + + WinHttpCloseHandle( req ); + WinHttpCloseHandle( con ); + WinHttpCloseHandle( ses ); +} + static void test_credentials(void) { static WCHAR userW[] = {'u','s','e','r',0}; @@ -2095,6 +2137,574 @@ WinHttpCloseHandle(ses); } +static void test_IWinHttpRequest(void) +{ + static const WCHAR usernameW[] = {'u','s','e','r','n','a','m','e',0}; + static const WCHAR passwordW[] = {'p','a','s','s','w','o','r','d',0}; + static const WCHAR url1W[] = {'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0}; + static const WCHAR url2W[] = {'w','i','n','e','h','q','.','o','r','g',0}; + static const WCHAR url3W[] = {'h','t','t','p',':','/','/','c','r','o','s','s','o','v','e','r','.', + 'c','o','d','e','w','e','a','v','e','r','s','.','c','o','m','/', + 'p','o','s','t','t','e','s','t','.','p','h','p',0}; + static const WCHAR method1W[] = {'G','E','T',0}; + static const WCHAR method2W[] = {'I','N','V','A','L','I','D',0}; + static const WCHAR method3W[] = {'P','O','S','T',0}; + static const WCHAR proxy_serverW[] = {'p','r','o','x','y','s','e','r','v','e','r',0}; + static const WCHAR bypas_listW[] = {'b','y','p','a','s','s','l','i','s','t',0}; + static const WCHAR connectionW[] = {'C','o','n','n','e','c','t','i','o','n',0}; + static const WCHAR dateW[] = {'D','a','t','e',0}; + static const WCHAR test_dataW[] = {'t','e','s','t','d','a','t','a',128,0}; + HRESULT hr; + IWinHttpRequest *req; + BSTR method, url, username, password, response = NULL, status_text = NULL, headers = NULL; + BSTR date, today, connection, value = NULL; + VARIANT async, empty, timeout, body, proxy_server, bypass_list, data; + VARIANT_BOOL succeeded; + LONG status; + WCHAR todayW[WINHTTP_TIME_FORMAT_BUFSIZE]; + SYSTEMTIME st; + + GetSystemTime( &st ); + WinHttpTimeFromSystemTime( &st, todayW ); + + CoInitialize( NULL ); + hr = CoCreateInstance( &CLSID_WinHttpRequest, NULL, CLSCTX_INPROC_SERVER, &IID_IWinHttpRequest, (void **)&req ); + ok( hr == S_OK, "got %08x\n", hr ); + + V_VT( &empty ) = VT_ERROR; + V_ERROR( &empty ) = 0xdeadbeef; + + V_VT( &async ) = VT_BOOL; + V_BOOL( &async ) = VARIANT_FALSE; + + method = SysAllocString( method3W ); + url = SysAllocString( url3W ); + hr = IWinHttpRequest_Open( req, method, url, async ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( method ); + SysFreeString( url ); + + V_VT( &data ) = VT_BSTR; + V_BSTR( &data ) = SysAllocString( test_dataW ); + hr = IWinHttpRequest_Send( req, data ); + ok( hr == S_OK || broken(hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE)), + "got %08x\n", hr ); + SysFreeString( V_BSTR( &data ) ); + + hr = IWinHttpRequest_Open( req, NULL, NULL, empty ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + method = SysAllocString( method1W ); + hr = IWinHttpRequest_Open( req, method, NULL, empty ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_Open( req, method, NULL, async ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + url = SysAllocString( url1W ); + hr = IWinHttpRequest_Open( req, NULL, url, empty ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_Abort( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Open( req, method, url, empty ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Abort( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Release( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = CoCreateInstance( &CLSID_WinHttpRequest, NULL, CLSCTX_INPROC_SERVER, &IID_IWinHttpRequest, (void **)&req ); + ok( hr == S_OK, "got %08x\n", hr ); + + SysFreeString( url ); + url = SysAllocString( url2W ); + hr = IWinHttpRequest_Open( req, method, url, async ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_UNRECOGNIZED_SCHEME ), "got %08x\n", hr ); + + SysFreeString( method ); + method = SysAllocString( method2W ); + hr = IWinHttpRequest_Open( req, method, url, async ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_UNRECOGNIZED_SCHEME ), "got %08x\n", hr ); + + SysFreeString( method ); + method = SysAllocString( method1W ); + SysFreeString( url ); + url = SysAllocString( url1W ); + hr = IWinHttpRequest_Open( req, method, url, async ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Abort( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Send( req, empty ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN ), "got %08x\n", hr ); + + hr = IWinHttpRequest_Abort( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Release( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = CoCreateInstance( &CLSID_WinHttpRequest, NULL, CLSCTX_INPROC_SERVER, &IID_IWinHttpRequest, (void **)&req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, &response ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_get_Status( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_Status( req, &status ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_get_StatusText( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_StatusText( req, &status_text ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseBody( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetTimeouts( req, 10000, 10000, 10000, 10000 ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetCredentials( req, NULL, NULL, 0xdeadbeef ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN ), "got %08x\n", hr ); + + VariantInit( &proxy_server ); + V_VT( &proxy_server ) = VT_ERROR; + VariantInit( &bypass_list ); + V_VT( &bypass_list ) = VT_ERROR; + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_DIRECT, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_PROXY, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_DIRECT, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetAllResponseHeaders( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetAllResponseHeaders( req, &headers ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_GetResponseHeader( req, NULL, NULL ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + connection = SysAllocString( connectionW ); + hr = IWinHttpRequest_GetResponseHeader( req, connection, NULL ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_GetResponseHeader( req, connection, &value ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_SetRequestHeader( req, NULL, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + date = SysAllocString( dateW ); + hr = IWinHttpRequest_SetRequestHeader( req, date, NULL ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN ), "got %08x\n", hr ); + + today = SysAllocString( todayW ); + hr = IWinHttpRequest_SetRequestHeader( req, date, today ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN ), "got %08x\n", hr ); + + hr = IWinHttpRequest_SetAutoLogonPolicy( req, 0xdeadbeef ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetAutoLogonPolicy( req, AutoLogonPolicy_OnlyIfBypassProxy ); + ok( hr == S_OK, "got %08x\n", hr ); + + SysFreeString( method ); + method = SysAllocString( method1W ); + SysFreeString( url ); + url = SysAllocString( url1W ); + hr = IWinHttpRequest_Open( req, method, url, async ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, &response ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_get_Status( req, &status ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_get_StatusText( req, &status_text ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseBody( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetTimeouts( req, 10000, 10000, 10000, 10000 ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetCredentials( req, NULL, NULL, 0xdeadbeef ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + username = SysAllocString( usernameW ); + hr = IWinHttpRequest_SetCredentials( req, username, NULL, 0xdeadbeef ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + password = SysAllocString( passwordW ); + hr = IWinHttpRequest_SetCredentials( req, NULL, password, 0xdeadbeef ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetCredentials( req, username, password, 0xdeadbeef ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetCredentials( req, NULL, password, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetCredentials( req, username, password, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER ); + ok( hr == S_OK, "got %08x\n", hr ); + + V_VT( &proxy_server ) = VT_BSTR; + V_BSTR( &proxy_server ) = SysAllocString( proxy_serverW ); + V_VT( &bypass_list ) = VT_BSTR; + V_BSTR( &bypass_list ) = SysAllocString( bypas_listW ); + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_PROXY, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, 0xdeadbeef, proxy_server, bypass_list ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_DIRECT, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetAllResponseHeaders( req, &headers ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_GetResponseHeader( req, connection, &value ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_SetRequestHeader( req, date, today ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetRequestHeader( req, date, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetAutoLogonPolicy( req, AutoLogonPolicy_OnlyIfBypassProxy ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Send( req, empty ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Send( req, empty ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, &response ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( response ); + + hr = IWinHttpRequest_get_Status( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + status = 0; + hr = IWinHttpRequest_get_Status( req, &status ); + ok( hr == S_OK, "got %08x\n", hr ); + trace("%d\n", status); + + hr = IWinHttpRequest_get_StatusText( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_StatusText( req, &status_text ); + ok( hr == S_OK, "got %08x\n", hr ); + trace("%s\n", wine_dbgstr_w(status_text)); + SysFreeString( status_text ); + + hr = IWinHttpRequest_get_ResponseBody( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetCredentials( req, username, password, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_PROXY, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_DIRECT, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetAllResponseHeaders( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetAllResponseHeaders( req, &headers ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( headers ); + + hr = IWinHttpRequest_GetResponseHeader( req, NULL, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetResponseHeader( req, connection, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetResponseHeader( req, connection, &value ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( value ); + + hr = IWinHttpRequest_SetRequestHeader( req, date, today ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_SetAutoLogonPolicy( req, AutoLogonPolicy_OnlyIfBypassProxy ); + ok( hr == S_OK, "got %08x\n", hr ); + + VariantInit( &timeout ); + V_VT( &timeout ) = VT_I4; + V_I4( &timeout ) = 10; + hr = IWinHttpRequest_WaitForResponse( req, timeout, &succeeded ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_Status( req, &status ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_StatusText( req, &status_text ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( status_text ); + + hr = IWinHttpRequest_SetCredentials( req, username, password, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_PROXY, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_DIRECT, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Send( req, empty ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + hr = IWinHttpRequest_get_ResponseText( req, &response ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( response ); + + hr = IWinHttpRequest_get_ResponseBody( req, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + VariantInit( &body ); + V_VT( &body ) = VT_ERROR; + hr = IWinHttpRequest_get_ResponseBody( req, &body ); + ok( hr == S_OK, "got %08x\n", hr ); + ok( V_VT( &body ) == (VT_ARRAY|VT_UI1), "got %08x\n", V_VT( &body ) ); + + hr = VariantClear( &body ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_PROXY, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_SetProxy( req, HTTPREQUEST_PROXYSETTING_DIRECT, proxy_server, bypass_list ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_GetAllResponseHeaders( req, &headers ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( headers ); + + hr = IWinHttpRequest_GetResponseHeader( req, connection, &value ); + ok( hr == S_OK, "got %08x\n", hr ); + SysFreeString( value ); + + hr = IWinHttpRequest_SetRequestHeader( req, date, today ); + ok( hr == HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND ), "got %08x\n", hr ); + + hr = IWinHttpRequest_SetAutoLogonPolicy( req, AutoLogonPolicy_OnlyIfBypassProxy ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Send( req, empty ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Abort( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Abort( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = IWinHttpRequest_Release( req ); + ok( hr == S_OK, "got %08x\n", hr ); + + SysFreeString( method ); + SysFreeString( url ); + SysFreeString( username ); + SysFreeString( password ); + SysFreeString( connection ); + SysFreeString( date ); + SysFreeString( today ); + VariantClear( &proxy_server ); + VariantClear( &bypass_list ); + CoUninitialize(); +} + +static void test_WinHttpDetectAutoProxyConfigUrl(void) +{ + BOOL ret; + WCHAR *url; + DWORD error; + +if (0) /* crashes on some win2k systems */ +{ + SetLastError(0xdeadbeef); + ret = WinHttpDetectAutoProxyConfigUrl( 0, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); +} + url = NULL; + SetLastError(0xdeadbeef); + ret = WinHttpDetectAutoProxyConfigUrl( 0, &url ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + +if (0) /* crashes on some win2k systems */ +{ + SetLastError(0xdeadbeef); + ret = WinHttpDetectAutoProxyConfigUrl( WINHTTP_AUTO_DETECT_TYPE_DNS_A, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); +} + url = NULL; + SetLastError(0xdeadbeef); + ret = WinHttpDetectAutoProxyConfigUrl( WINHTTP_AUTO_DETECT_TYPE_DNS_A, &url ); + error = GetLastError(); + if (!ret) + ok( error == ERROR_WINHTTP_AUTODETECTION_FAILED, "got %u\n", error ); + else + { + trace("%s\n", wine_dbgstr_w(url)); + GlobalFree( url ); + } +} + +static void test_WinHttpGetIEProxyConfigForCurrentUser(void) +{ + BOOL ret; + DWORD error; + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG cfg; + + memset( &cfg, 0, sizeof(cfg) ); + + SetLastError(0xdeadbeef); + ret = WinHttpGetIEProxyConfigForCurrentUser( NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + ret = WinHttpGetIEProxyConfigForCurrentUser( &cfg ); + ok( ret, "expected success\n" ); + trace("%d\n", cfg.fAutoDetect); + trace("%s\n", wine_dbgstr_w(cfg.lpszAutoConfigUrl)); + trace("%s\n", wine_dbgstr_w(cfg.lpszProxy)); + trace("%s\n", wine_dbgstr_w(cfg.lpszProxyBypass)); + GlobalFree( cfg.lpszAutoConfigUrl ); + GlobalFree( cfg.lpszProxy ); + GlobalFree( cfg.lpszProxyBypass ); +} + +static void test_WinHttpGetProxyForUrl(void) +{ + static const WCHAR urlW[] = {'h','t','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g',0}; + static const WCHAR emptyW[] = {0}; + BOOL ret; + DWORD error; + HINTERNET session; + WINHTTP_AUTOPROXY_OPTIONS options; + WINHTTP_PROXY_INFO info; + + memset( &options, 0, sizeof(options) ); + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( NULL, NULL, NULL, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_HANDLE, "got %u\n", error ); + + session = WinHttpOpen( test_useragent, 0, NULL, NULL, 0 ); + ok( session != NULL, "failed to open session %u\n", GetLastError() ); + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, NULL, NULL, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, emptyW, NULL, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, urlW, NULL, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, urlW, &options, &info ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DNS_A; + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, urlW, &options, NULL ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + options.dwAutoDetectFlags = 0; + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, urlW, &options, &info ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_CONFIG_URL; + options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DNS_A; + + SetLastError(0xdeadbeef); + ret = WinHttpGetProxyForUrl( session, urlW, &options, &info ); + error = GetLastError(); + ok( !ret, "expected failure\n" ); + ok( error == ERROR_INVALID_PARAMETER, "got %u\n", error ); + + options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; + options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DNS_A; + + memset( &info, 0, sizeof(info) ); + ret = WinHttpGetProxyForUrl( session, urlW, &options, &info ); + if (ret) + { + trace("%u\n", info.dwAccessType); + trace("%s\n", wine_dbgstr_w(info.lpszProxy)); + trace("%s\n", wine_dbgstr_w(info.lpszProxyBypass)); + GlobalFree( (WCHAR *)info.lpszProxy ); + GlobalFree( (WCHAR *)info.lpszProxyBypass ); + } + WinHttpCloseHandle( session ); +} + START_TEST (winhttp) { static const WCHAR basicW[] = {'/','b','a','s','i','c',0}; @@ -2116,6 +2726,10 @@ test_Timeouts(); test_resolve_timeout(); test_credentials(); + test_IWinHttpRequest(); + test_WinHttpDetectAutoProxyConfigUrl(); + test_WinHttpGetIEProxyConfigForCurrentUser(); + test_WinHttpGetProxyForUrl(); si.event = CreateEvent(NULL, 0, 0, NULL); si.port = 7532; @@ -2131,6 +2745,7 @@ test_basic_request(si.port, NULL, basicW); test_no_headers(si.port); test_basic_authentication(si.port); + test_bad_header(si.port); /* send the basic request again to shutdown the server thread */ test_basic_request(si.port, NULL, quitW); Index: dll/win32/winhttp/CMakeLists.txt =================================================================== --- dll/win32/winhttp/CMakeLists.txt (revision 56875) +++ dll/win32/winhttp/CMakeLists.txt (working copy) @@ -1,5 +1,9 @@ -add_definitions(-D__WINESRC__) +add_typelib(winhttp_tlb.idl) + +add_definitions( + -D__WINESRC__ + -D_WINE) include_directories(${REACTOS_SOURCE_DIR}/include/reactos/wine) spec2def(winhttp.dll winhttp.spec ADD_IMPORTLIB) @@ -12,17 +16,19 @@ request.c session.c url.c + rsrc.rc ${CMAKE_CURRENT_BINARY_DIR}/winhttp.def) +set_source_files_properties(rsrc.rc PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/winhttp_tlb.tlb) + add_library(winhttp SHARED ${SOURCE}) set_module_type(winhttp win32dll) -target_link_libraries(winhttp wine) -add_delay_importlibs(winhttp crypt32) -add_importlibs(winhttp shlwapi wininet ws2_32 msvcrt advapi32 kernel32 ntdll) +target_link_libraries(winhttp uuid wine) +add_delay_importlibs(winhttp oleaut32 ole32 crypt32) +add_importlibs(winhttp user32 advapi32 ws2_32 msvcrt kernel32 ntdll) +# wininet_tlb.tlb needs stdole2.tlb +add_dependencies(winhttp stdole2) + add_cd_file(TARGET winhttp DESTINATION reactos/system32 FOR all) - -if(NOT MSVC) - allow_warnings(winhttp) -endif() Index: dll/win32/winhttp/handle.c =================================================================== --- dll/win32/winhttp/handle.c (revision 56875) +++ dll/win32/winhttp/handle.c (working copy) @@ -103,7 +103,7 @@ } if (max_handles == next_handle) { - num = max_handles + HANDLE_CHUNK_SIZE; + num = max_handles * 2; if (!(p = heap_realloc_zero( handles, sizeof(ULONG_PTR) * num ))) goto end; handles = p; max_handles = num; Index: dll/win32/winhttp/main.c =================================================================== --- dll/win32/winhttp/main.c (revision 56875) +++ dll/win32/winhttp/main.c (working copy) @@ -16,19 +16,22 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS #include "config.h" - #include #include "windef.h" #include "winbase.h" #include "objbase.h" +#include "rpcproxy.h" +#include "httprequest.h" #include "winhttp.h" #include "wine/debug.h" - #include "winhttp_private.h" +static HINSTANCE instance; + WINE_DEFAULT_DEBUG_CHANNEL(winhttp); /****************************************************************** @@ -39,6 +42,7 @@ switch(fdwReason) { case DLL_PROCESS_ATTACH: + instance = hInstDLL; DisableThreadLibraryCalls(hInstDLL); break; case DLL_PROCESS_DETACH: @@ -48,13 +52,109 @@ return TRUE; } +typedef HRESULT (*fnCreateInstance)( IUnknown *outer, void **obj ); + +struct winhttp_cf +{ + IClassFactory IClassFactory_iface; + fnCreateInstance pfnCreateInstance; +}; + +static inline struct winhttp_cf *impl_from_IClassFactory( IClassFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct winhttp_cf, IClassFactory_iface ); +} + +static HRESULT WINAPI requestcf_QueryInterface( + IClassFactory *iface, + REFIID riid, + void **obj ) +{ + if (IsEqualGUID( riid, &IID_IUnknown ) || + IsEqualGUID( riid, &IID_IClassFactory )) + { + IClassFactory_AddRef( iface ); + *obj = iface; + return S_OK; + } + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI requestcf_AddRef( + IClassFactory *iface ) +{ + return 2; +} + +static ULONG WINAPI requestcf_Release( + IClassFactory *iface ) +{ + return 1; +} + +static HRESULT WINAPI requestcf_CreateInstance( + IClassFactory *iface, + LPUNKNOWN outer, + REFIID riid, + void **obj ) +{ + struct winhttp_cf *cf = impl_from_IClassFactory( iface ); + IUnknown *unknown; + HRESULT hr; + + TRACE("%p, %s, %p\n", outer, debugstr_guid(riid), obj); + + *obj = NULL; + if (outer) + return CLASS_E_NOAGGREGATION; + + hr = cf->pfnCreateInstance( outer, (void **)&unknown ); + if (FAILED(hr)) + return hr; + + hr = IUnknown_QueryInterface( unknown, riid, obj ); + if (FAILED(hr)) + return hr; + + IUnknown_Release( unknown ); + return hr; +} + +static HRESULT WINAPI requestcf_LockServer( + IClassFactory *iface, + BOOL dolock ) +{ + FIXME("%p, %d\n", iface, dolock); + return S_OK; +} + +static const struct IClassFactoryVtbl winhttp_cf_vtbl = +{ + requestcf_QueryInterface, + requestcf_AddRef, + requestcf_Release, + requestcf_CreateInstance, + requestcf_LockServer +}; + +static struct winhttp_cf request_cf = { { &winhttp_cf_vtbl }, WinHttpRequest_create }; + /****************************************************************** * DllGetClassObject (winhttp.@) */ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) { - FIXME("(%s %s %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); - return CLASS_E_CLASSNOTAVAILABLE; + IClassFactory *cf = NULL; + + TRACE("%s, %s, %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + if (IsEqualGUID( rclsid, &CLSID_WinHttpRequest )) + { + cf = &request_cf.IClassFactory_iface; + } + if (!cf) return CLASS_E_CLASSNOTAVAILABLE; + return IClassFactory_QueryInterface( cf, riid, ppv ); } /****************************************************************** @@ -70,8 +170,7 @@ */ HRESULT WINAPI DllRegisterServer(void) { - FIXME("()\n"); - return S_OK; + return __wine_register_resources( instance ); } /*********************************************************************** @@ -79,6 +178,5 @@ */ HRESULT WINAPI DllUnregisterServer(void) { - FIXME("()\n"); - return S_OK; + return __wine_unregister_resources( instance ); } Index: dll/win32/winhttp/net.c =================================================================== --- dll/win32/winhttp/net.c (revision 56875) +++ dll/win32/winhttp/net.c (working copy) @@ -116,6 +116,7 @@ MAKE_FUNCPTR( SSL_shutdown ); MAKE_FUNCPTR( SSL_write ); MAKE_FUNCPTR( SSL_read ); +MAKE_FUNCPTR( SSL_pending ); MAKE_FUNCPTR( SSL_get_error ); MAKE_FUNCPTR( SSL_get_ex_new_index ); MAKE_FUNCPTR( SSL_get_ex_data ); @@ -159,7 +160,7 @@ #endif /* translate a unix error code into a winsock error code */ -#if 0 +#ifndef __REACTOS__ static int sock_get_error( int err ) { #if !defined(__MINGW32__) && !defined (_MSC_VER) @@ -227,6 +228,12 @@ } #else #define sock_get_error(x) WSAGetLastError() + +static inline int unix_ioctl(int filedes, long request, void *arg) +{ + return ioctlsocket(filedes, request, arg); +} +#define ioctlsocket unix_ioctl #endif #ifdef SONAME_LIBSSL @@ -463,6 +470,7 @@ LOAD_FUNCPTR( SSL_shutdown ); LOAD_FUNCPTR( SSL_write ); LOAD_FUNCPTR( SSL_read ); + LOAD_FUNCPTR( SSL_pending ); LOAD_FUNCPTR( SSL_get_error ); LOAD_FUNCPTR( SSL_get_ex_new_index ); LOAD_FUNCPTR( SSL_get_ex_data ); @@ -536,14 +544,18 @@ pCRYPTO_set_id_callback(ssl_thread_id); num_ssl_locks = pCRYPTO_num_locks(); - ssl_locks = HeapAlloc(GetProcessHeap(), 0, num_ssl_locks * sizeof(CRITICAL_SECTION)); + ssl_locks = heap_alloc(num_ssl_locks * sizeof(CRITICAL_SECTION)); if (!ssl_locks) { set_last_error( ERROR_OUTOFMEMORY ); LeaveCriticalSection( &init_ssl_cs ); return FALSE; } - for (i = 0; i < num_ssl_locks; i++) InitializeCriticalSection( &ssl_locks[i] ); + for (i = 0; i < num_ssl_locks; i++) + { + InitializeCriticalSection( &ssl_locks[i] ); + ssl_locks[i].DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ssl_locks"); + } pCRYPTO_set_locking_callback(ssl_lock_callback); LeaveCriticalSection( &init_ssl_cs ); @@ -572,10 +584,18 @@ if (ssl_locks) { int i; - for (i = 0; i < num_ssl_locks; i++) DeleteCriticalSection( &ssl_locks[i] ); - HeapFree( GetProcessHeap(), 0, ssl_locks ); + for (i = 0; i < num_ssl_locks; i++) + { + ssl_locks[i].DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &ssl_locks[i] ); + } + heap_free( ssl_locks ); } + DeleteCriticalSection(&init_ssl_cs); #endif +#ifndef HAVE_GETADDRINFO + DeleteCriticalSection(&cs_gethostbyname); +#endif } BOOL netconn_connected( netconn_t *conn ) @@ -638,6 +658,7 @@ res = sock_get_error( errno ); if (res == WSAEWOULDBLOCK || res == WSAEINPROGRESS) { + // ReactOS: use select instead of poll fd_set outfd; struct timeval tv; @@ -744,8 +765,6 @@ BOOL netconn_recv( netconn_t *conn, void *buf, size_t len, int flags, int *recvd ) { - int ret; - *recvd = 0; if (!netconn_connected( conn )) return FALSE; if (!len) return TRUE; @@ -753,6 +772,8 @@ if (conn->secure) { #ifdef SONAME_LIBSSL + int ret; + if (flags & ~(MSG_PEEK | MSG_WAITALL)) FIXME("SSL_read does not support the following flags: %08x\n", flags); @@ -806,7 +827,7 @@ } else memcpy( conn->peek_msg, buf, ret ); } - *recvd = ret; + *recvd += ret; return TRUE; #else return FALSE; @@ -831,7 +852,7 @@ if (conn->secure) { #ifdef SONAME_LIBSSL - if (conn->peek_msg) *available = conn->peek_len; + *available = pSSL_pending( conn->ssl_conn ) + conn->peek_len; #endif return TRUE; } @@ -843,6 +864,7 @@ BOOL netconn_get_next_line( netconn_t *conn, char *buffer, DWORD *buflen ) { + // ReactOS: use select instead of poll fd_set infd; BOOL ret = FALSE; DWORD recvd = 0; Index: dll/win32/winhttp/pac.js =================================================================== --- dll/win32/winhttp/pac.js (revision 0) +++ dll/win32/winhttp/pac.js (working copy) @@ -0,0 +1,244 @@ +/* + * Copyright 2011 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Based on nsProxyAutoConfig.js from mozilla.org. + */ + +function myIpAddress() { + try { + return dns_resolve(''); + } catch (e) { + return '127.0.0.1'; + } +} + +function dnsResolve(host) { + try { + return dns_resolve(host); + } catch (e) { + return null; + } +} + +function dnsDomainIs(host, domain) { + return (host.length >= domain.length && + host.substring(host.length - domain.length) == domain); +} + +function dnsDomainLevels(host) { + return host.split('.').length-1; +} + +function convert_addr(ipchars) { + var bytes = ipchars.split('.'); + var result = ((bytes[0] & 0xff) << 24) | + ((bytes[1] & 0xff) << 16) | + ((bytes[2] & 0xff) << 8) | + (bytes[3] & 0xff); + return result; +} + +function isInNet(ipaddr, pattern, maskstr) { + var test = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(ipaddr); + if (test == null) { + ipaddr = dnsResolve(ipaddr); + if (ipaddr == null) + return false; + } else if (test[1] > 255 || test[2] > 255 || + test[3] > 255 || test[4] > 255) { + return false; // not an IP address + } + var host = convert_addr(ipaddr); + var pat = convert_addr(pattern); + var mask = convert_addr(maskstr); + return ((host & mask) == (pat & mask)); +} + +function isPlainHostName(host) { + return (host.search('\\.') == -1); +} + +function isResolvable(host) { + var ip = dnsResolve(host); + return (ip != null); +} + +function localHostOrDomainIs(host, hostdom) { + return (host == hostdom) || + (hostdom.lastIndexOf(host + '.', 0) == 0); +} + +function shExpMatch(url, pattern) { + pattern = pattern.replace(/\./g, '\\.'); + pattern = pattern.replace(/\*/g, '.*'); + pattern = pattern.replace(/\?/g, '.'); + var newRe = new RegExp('^'+pattern+'$'); + return newRe.test(url); +} + +var wdays = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6}; +var months = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11}; + +function weekdayRange() { + function getDay(weekday) { + if (weekday in wdays) { + return wdays[weekday]; + } + return -1; + } + var date = new Date(); + var argc = arguments.length; + var wday; + if (argc < 1) + return false; + if (arguments[argc - 1] == 'GMT') { + argc--; + wday = date.getUTCDay(); + } else { + wday = date.getDay(); + } + var wd1 = getDay(arguments[0]); + var wd2 = (argc == 2) ? getDay(arguments[1]) : wd1; + return (wd1 == -1 || wd2 == -1) ? false + : (wd1 <= wday && wday <= wd2); +} + +function dateRange() { + function getMonth(name) { + if (name in months) { + return months[name]; + } + return -1; + } + var date = new Date(); + var argc = arguments.length; + if (argc < 1) { + return false; + } + var isGMT = (arguments[argc - 1] == 'GMT'); + + if (isGMT) { + argc--; + } + // function will work even without explict handling of this case + if (argc == 1) { + var tmp = parseInt(arguments[0]); + if (isNaN(tmp)) { + return ((isGMT ? date.getUTCMonth() : date.getMonth()) == getMonth(arguments[0])); + } else if (tmp < 32) { + return ((isGMT ? date.getUTCDate() : date.getDate()) == tmp); + } else { + return ((isGMT ? date.getUTCFullYear() : date.getFullYear()) == tmp); + } + } + var year = date.getFullYear(); + var date1, date2; + date1 = new Date(year, 0, 1, 0, 0, 0); + date2 = new Date(year, 11, 31, 23, 59, 59); + var adjustMonth = false; + for (var i = 0; i < (argc >> 1); i++) { + var tmp = parseInt(arguments[i]); + if (isNaN(tmp)) { + var mon = getMonth(arguments[i]); + date1.setMonth(mon); + } else if (tmp < 32) { + adjustMonth = (argc <= 2); + date1.setDate(tmp); + } else { + date1.setFullYear(tmp); + } + } + for (var i = (argc >> 1); i < argc; i++) { + var tmp = parseInt(arguments[i]); + if (isNaN(tmp)) { + var mon = getMonth(arguments[i]); + date2.setMonth(mon); + } else if (tmp < 32) { + date2.setDate(tmp); + } else { + date2.setFullYear(tmp); + } + } + if (adjustMonth) { + date1.setMonth(date.getMonth()); + date2.setMonth(date.getMonth()); + } + if (isGMT) { + var tmp = date; + tmp.setFullYear(date.getUTCFullYear()); + tmp.setMonth(date.getUTCMonth()); + tmp.setDate(date.getUTCDate()); + tmp.setHours(date.getUTCHours()); + tmp.setMinutes(date.getUTCMinutes()); + tmp.setSeconds(date.getUTCSeconds()); + date = tmp; + } + return ((date1 <= date) && (date <= date2)); +} + +function timeRange() { + var argc = arguments.length; + var date = new Date(); + var isGMT= false; + + if (argc < 1) { + return false; + } + if (arguments[argc - 1] == 'GMT') { + isGMT = true; + argc--; + } + + var hour = isGMT ? date.getUTCHours() : date.getHours(); + var date1, date2; + date1 = new Date(); + date2 = new Date(); + + if (argc == 1) { + return (hour == arguments[0]); + } else if (argc == 2) { + return ((arguments[0] <= hour) && (hour <= arguments[1])); + } else { + switch (argc) { + case 6: + date1.setSeconds(arguments[2]); + date2.setSeconds(arguments[5]); + case 4: + var middle = argc >> 1; + date1.setHours(arguments[0]); + date1.setMinutes(arguments[1]); + date2.setHours(arguments[middle]); + date2.setMinutes(arguments[middle + 1]); + if (middle == 2) { + date2.setSeconds(59); + } + break; + default: + throw 'timeRange: bad number of arguments' + } + } + + if (isGMT) { + date.setFullYear(date.getUTCFullYear()); + date.setMonth(date.getUTCMonth()); + date.setDate(date.getUTCDate()); + date.setHours(date.getUTCHours()); + date.setMinutes(date.getUTCMinutes()); + date.setSeconds(date.getUTCSeconds()); + } + return ((date1 <= date) && (date <= date2)); +} Index: dll/win32/winhttp/pac.js =================================================================== --- dll/win32/winhttp/pac.js (revision 0) +++ dll/win32/winhttp/pac.js (working copy) Property changes on: dll/win32/winhttp/pac.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: dll/win32/winhttp/request.c =================================================================== --- dll/win32/winhttp/request.c (revision 56875) +++ dll/win32/winhttp/request.c (working copy) @@ -1,7 +1,7 @@ /* * Copyright 2004 Mike McCormack for CodeWeavers * Copyright 2006 Rob Shearman for CodeWeavers - * Copyright 2008 Hans Leidekker for CodeWeavers + * Copyright 2008, 2011 Hans Leidekker for CodeWeavers * Copyright 2009 Juan Lang * * This library is free software; you can redistribute it and/or @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS #include "config.h" #include "wine/port.h" #include "wine/debug.h" @@ -30,6 +31,9 @@ #include "windef.h" #include "winbase.h" +#include "ole2.h" +#include "initguid.h" +#include "httprequest.h" #include "winhttp.h" #include "winhttp_private.h" @@ -423,6 +427,11 @@ q = p; while (*q) { + if (q[0] == '\n' && q[1] == '\r') + { + q[0] = '\r'; + q[1] = '\n'; + } if (q[0] == '\r' && q[1] == '\n') break; q++; } @@ -907,53 +916,68 @@ #define INET6_ADDRSTRLEN 46 #endif +static WCHAR *addr_to_str( const struct sockaddr *addr ) +{ + char buf[INET6_ADDRSTRLEN]; + const void *src; + + switch (addr->sa_family) + { + case AF_INET: + src = &((struct sockaddr_in *)addr)->sin_addr; + break; + case AF_INET6: + src = &((struct sockaddr_in6 *)addr)->sin6_addr; + break; + default: + WARN("unsupported address family %d\n", addr->sa_family); + return NULL; + } + if (!inet_ntop( addr->sa_family, src, buf, sizeof(buf) )) return NULL; + return strdupAW( buf ); +} + static BOOL open_connection( request_t *request ) { connect_t *connect; - const void *addr; - char address[INET6_ADDRSTRLEN]; - WCHAR *addressW; + WCHAR *addressW = NULL; INTERNET_PORT port; socklen_t slen; + struct sockaddr *saddr; + DWORD len; if (netconn_connected( &request->netconn )) return TRUE; connect = request->connect; port = connect->serverport ? connect->serverport : (request->hdr.flags & WINHTTP_FLAG_SECURE ? 443 : 80); + saddr = (struct sockaddr *)&connect->sockaddr; + slen = sizeof(struct sockaddr); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, strlenW(connect->servername) + 1 ); - - slen = sizeof(connect->sockaddr); - if (!netconn_resolve( connect->servername, port, (struct sockaddr *)&connect->sockaddr, &slen, request->resolve_timeout )) return FALSE; - switch (connect->sockaddr.ss_family) + if (!connect->resolved) { - case AF_INET: - addr = &((struct sockaddr_in *)&connect->sockaddr)->sin_addr; - break; - case AF_INET6: - addr = &((struct sockaddr_in6 *)&connect->sockaddr)->sin6_addr; - break; - default: - WARN("unsupported address family %d\n", connect->sockaddr.ss_family); - return FALSE; - } - inet_ntop( connect->sockaddr.ss_family, addr, address, sizeof(address) ); - addressW = strdupAW( address ); + len = strlenW( connect->servername ) + 1; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESOLVING_NAME, connect->servername, len ); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, strlenW(addressW) + 1 ); + if (!netconn_resolve( connect->servername, port, saddr, &slen, request->resolve_timeout )) return FALSE; + connect->resolved = TRUE; - TRACE("connecting to %s:%u\n", address, port); + if (!(addressW = addr_to_str( saddr ))) return FALSE; + len = strlenW( addressW ) + 1; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len ); + } + if (!addressW && !(addressW = addr_to_str( saddr ))) return FALSE; + TRACE("connecting to %s:%u\n", debugstr_w(addressW), port); send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 ); - if (!netconn_create( &request->netconn, connect->sockaddr.ss_family, SOCK_STREAM, 0 )) + if (!netconn_create( &request->netconn, saddr->sa_family, SOCK_STREAM, 0 )) { heap_free( addressW ); return FALSE; } netconn_set_timeout( &request->netconn, TRUE, request->send_timeout ); netconn_set_timeout( &request->netconn, FALSE, request->recv_timeout ); - if (!netconn_connect( &request->netconn, (struct sockaddr *)&connect->sockaddr, slen, request->connect_timeout )) + if (!netconn_connect( &request->netconn, saddr, slen, request->connect_timeout )) { netconn_close( &request->netconn ); heap_free( addressW ); @@ -1029,8 +1053,13 @@ WCHAR *req = NULL; char *req_ascii; int bytes_sent; - DWORD len; + DWORD len, i, flags; + flags = WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA; + for (i = 0; i < request->num_accept_types; i++) + { + process_header( request, attr_accept, request->accept_types[i], flags, TRUE ); + } if (session->agent) process_header( request, attr_user_agent, session->agent, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW, TRUE ); @@ -1063,6 +1092,8 @@ return FALSE; } + if (context) request->hdr.context = context; + if (!(ret = open_connection( request ))) goto end; if (!(req = build_request_string( request ))) goto end; @@ -1070,7 +1101,6 @@ TRACE("full request: %s\n", debugstr_a(req_ascii)); len = strlen(req_ascii); - if (context) request->hdr.context = context; send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST, NULL, 0 ); ret = netconn_send( &request->netconn, req_ascii, len, 0, &bytes_sent ); @@ -1193,7 +1223,7 @@ size = 0; query_headers( request, level, NULL, NULL, &size, &index ); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) break; + if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) break; index--; if (!(buffer = heap_alloc( size ))) return FALSE; @@ -1546,7 +1576,7 @@ return TRUE; } -static BOOL handle_redirect( request_t *request ) +static BOOL handle_redirect( request_t *request, DWORD status ) { BOOL ret = FALSE; DWORD size, len; @@ -1628,9 +1658,11 @@ if ((index = get_header_index( request, attr_content_type, 0, TRUE )) >= 0) delete_header( request, index ); if ((index = get_header_index( request, attr_content_length, 0, TRUE )) >= 0 ) delete_header( request, index ); - /* redirects are always GET requests */ - heap_free( request->verb ); - request->verb = strdupW( getW ); + if (status != HTTP_STATUS_REDIRECT_KEEP_VERB && !strcmpW( request->verb, postW )) + { + heap_free( request->verb ); + request->verb = strdupW( getW ); + } ret = TRUE; end: @@ -1796,7 +1828,11 @@ DWORD bytes_read; char buffer[2048]; - if (!request->content_length) return; + if (!request->content_length) + { + finished_reading( request ); + return; + } for (;;) { if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return; @@ -1824,8 +1860,11 @@ for (;;) { - if (!(ret = read_reply( request ))) break; - + if (!(ret = read_reply( request ))) + { + set_last_error( ERROR_WINHTTP_INVALID_SERVER_RESPONSE ); + break; + } size = sizeof(DWORD); query = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER; if (!(ret = query_headers( request, query, NULL, &status, &size, NULL ))) break; @@ -1842,10 +1881,10 @@ if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS) break; drain_content( request ); - if (!(ret = handle_redirect( request ))) break; + if (!(ret = handle_redirect( request, status ))) break; clear_response_headers( request ); - ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */ + send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); /* recurse synchronously */ continue; } else if (status == 401 || status == 407) @@ -1859,7 +1898,7 @@ break; } clear_response_headers( request ); - ret = send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); + send_request( request, NULL, 0, NULL, 0, 0, 0, FALSE ); continue; } break; @@ -2132,3 +2171,1264 @@ release_object( &request->hdr ); return ret; } + +enum request_state +{ + REQUEST_STATE_UNINITIALIZED, + REQUEST_STATE_INITIALIZED, + REQUEST_STATE_CANCELLED, + REQUEST_STATE_OPEN, + REQUEST_STATE_SENT, + REQUEST_STATE_RESPONSE_RECEIVED +}; + +struct winhttp_request +{ + IWinHttpRequest IWinHttpRequest_iface; + LONG refs; + CRITICAL_SECTION cs; + enum request_state state; + HINTERNET hsession; + HINTERNET hconnect; + HINTERNET hrequest; + VARIANT data; + WCHAR *verb; + HANDLE thread; + HANDLE wait; + HANDLE cancel; + char *buffer; + DWORD offset; + DWORD bytes_available; + DWORD bytes_read; + DWORD error; + DWORD logon_policy; + DWORD disable_feature; + LONG resolve_timeout; + LONG connect_timeout; + LONG send_timeout; + LONG receive_timeout; + WINHTTP_PROXY_INFO proxy; +}; + +static inline struct winhttp_request *impl_from_IWinHttpRequest( IWinHttpRequest *iface ) +{ + return CONTAINING_RECORD( iface, struct winhttp_request, IWinHttpRequest_iface ); +} + +static ULONG WINAPI winhttp_request_AddRef( + IWinHttpRequest *iface ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + return InterlockedIncrement( &request->refs ); +} + +/* critical section must be held */ +static void cancel_request( struct winhttp_request *request ) +{ + if (request->state <= REQUEST_STATE_CANCELLED) return; + if (request->thread) SetEvent( request->cancel ); + request->state = REQUEST_STATE_CANCELLED; +} + +/* critical section must be held */ +static void free_request( struct winhttp_request *request ) +{ + if (request->state < REQUEST_STATE_INITIALIZED) return; + WinHttpCloseHandle( request->hrequest ); + WinHttpCloseHandle( request->hconnect ); + WinHttpCloseHandle( request->hsession ); + CloseHandle( request->thread ); + CloseHandle( request->wait ); + CloseHandle( request->cancel ); + heap_free( (WCHAR *)request->proxy.lpszProxy ); + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + heap_free( request->buffer ); + heap_free( request->verb ); + VariantClear( &request->data ); +} + +static ULONG WINAPI winhttp_request_Release( + IWinHttpRequest *iface ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + LONG refs = InterlockedDecrement( &request->refs ); + if (!refs) + { + TRACE("destroying %p\n", request); + + EnterCriticalSection( &request->cs ); + cancel_request( request ); + free_request( request ); + LeaveCriticalSection( &request->cs ); + request->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &request->cs ); + heap_free( request ); + } + return refs; +} + +static HRESULT WINAPI winhttp_request_QueryInterface( + IWinHttpRequest *iface, + REFIID riid, + void **obj ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p, %s, %p\n", request, debugstr_guid(riid), obj ); + + if (IsEqualGUID( riid, &IID_IWinHttpRequest ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IUnknown )) + { + *obj = iface; + } + else + { + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + IWinHttpRequest_AddRef( iface ); + return S_OK; +} + +static HRESULT WINAPI winhttp_request_GetTypeInfoCount( + IWinHttpRequest *iface, + UINT *count ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p, %p\n", request, count); + *count = 1; + return S_OK; +} + +enum type_id +{ + IWinHttpRequest_tid, + last_tid +}; + +static ITypeLib *winhttp_typelib; +static ITypeInfo *winhttp_typeinfo[last_tid]; + +static REFIID winhttp_tid_id[] = +{ + &IID_IWinHttpRequest +}; + +static HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret ) +{ + HRESULT hr; + + if (!winhttp_typelib) + { + ITypeLib *typelib; + + hr = LoadRegTypeLib( &LIBID_WinHttp, 5, 1, LOCALE_SYSTEM_DEFAULT, &typelib ); + if (FAILED(hr)) + { + ERR("LoadRegTypeLib failed: %08x\n", hr); + return hr; + } + if (InterlockedCompareExchangePointer( (void **)&winhttp_typelib, typelib, NULL )) + ITypeLib_Release( typelib ); + } + if (!winhttp_typeinfo[tid]) + { + ITypeInfo *typeinfo; + + hr = ITypeLib_GetTypeInfoOfGuid( winhttp_typelib, winhttp_tid_id[tid], &typeinfo ); + if (FAILED(hr)) + { + ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(winhttp_tid_id[tid]), hr); + return hr; + } + if (InterlockedCompareExchangePointer( (void **)(winhttp_typeinfo + tid), typeinfo, NULL )) + ITypeInfo_Release( typeinfo ); + } + *ret = winhttp_typeinfo[tid]; + return S_OK; +} + +static HRESULT WINAPI winhttp_request_GetTypeInfo( + IWinHttpRequest *iface, + UINT index, + LCID lcid, + ITypeInfo **info ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + TRACE("%p, %u, %u, %p\n", request, index, lcid, info); + + return get_typeinfo( IWinHttpRequest_tid, info ); +} + +static HRESULT WINAPI winhttp_request_GetIDsOfNames( + IWinHttpRequest *iface, + REFIID riid, + LPOLESTR *names, + UINT count, + LCID lcid, + DISPID *dispid ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p, %s, %p, %u, %u, %p\n", request, debugstr_guid(riid), names, count, lcid, dispid); + + if (!names || !count || !dispid) return E_INVALIDARG; + + hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames( typeinfo, names, count, dispid ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI winhttp_request_Invoke( + IWinHttpRequest *iface, + DISPID member, + REFIID riid, + LCID lcid, + WORD flags, + DISPPARAMS *params, + VARIANT *result, + EXCEPINFO *excep_info, + UINT *arg_err ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p, %d, %s, %d, %d, %p, %p, %p, %p\n", request, member, debugstr_guid(riid), + lcid, flags, params, result, excep_info, arg_err); + + hr = get_typeinfo( IWinHttpRequest_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke( typeinfo, &request->IWinHttpRequest_iface, member, flags, + params, result, excep_info, arg_err ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI winhttp_request_SetProxy( + IWinHttpRequest *iface, + HTTPREQUEST_PROXY_SETTING proxy_setting, + VARIANT proxy_server, + VARIANT bypass_list ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err = ERROR_SUCCESS; + + TRACE("%p, %u, %s, %s\n", request, proxy_setting, debugstr_variant(&proxy_server), + debugstr_variant(&bypass_list)); + + EnterCriticalSection( &request->cs ); + switch (proxy_setting) + { + case HTTPREQUEST_PROXYSETTING_DEFAULT: + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + heap_free( (WCHAR *)request->proxy.lpszProxy ); + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + break; + + case HTTPREQUEST_PROXYSETTING_DIRECT: + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; + heap_free( (WCHAR *)request->proxy.lpszProxy ); + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + break; + + case HTTPREQUEST_PROXYSETTING_PROXY: + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + if (V_VT( &proxy_server ) == VT_BSTR) + { + heap_free( (WCHAR *)request->proxy.lpszProxy ); + request->proxy.lpszProxy = strdupW( V_BSTR( &proxy_server ) ); + } + if (V_VT( &bypass_list ) == VT_BSTR) + { + heap_free( (WCHAR *)request->proxy.lpszProxyBypass ); + request->proxy.lpszProxyBypass = strdupW( V_BSTR( &bypass_list ) ); + } + break; + + default: + err = ERROR_INVALID_PARAMETER; + break; + } + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_SetCredentials( + IWinHttpRequest *iface, + BSTR username, + BSTR password, + HTTPREQUEST_SETCREDENTIALS_FLAGS flags ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD target, scheme = WINHTTP_AUTH_SCHEME_BASIC; /* FIXME: query supported schemes */ + DWORD err = ERROR_SUCCESS; + + TRACE("%p, %s, %p\n", request, debugstr_w(username), password); + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_OPEN) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN; + goto done; + } + switch (flags) + { + case HTTPREQUEST_SETCREDENTIALS_FOR_SERVER: + target = WINHTTP_AUTH_TARGET_SERVER; + break; + case HTTPREQUEST_SETCREDENTIALS_FOR_PROXY: + target = WINHTTP_AUTH_TARGET_PROXY; + break; + default: + err = ERROR_INVALID_PARAMETER; + goto done; + } + if (!WinHttpSetCredentials( request->hrequest, target, scheme, username, password, NULL )) + { + err = get_last_error(); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static void initialize_request( struct winhttp_request *request ) +{ + request->hrequest = NULL; + request->hconnect = NULL; + request->hsession = NULL; + request->thread = NULL; + request->wait = NULL; + request->cancel = NULL; + request->buffer = NULL; + request->verb = NULL; + request->offset = 0; + request->bytes_available = 0; + request->bytes_read = 0; + request->error = ERROR_SUCCESS; + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM; + request->disable_feature = WINHTTP_DISABLE_AUTHENTICATION; + request->proxy.dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + request->resolve_timeout = 0; + request->connect_timeout = 60000; + request->send_timeout = 30000; + request->receive_timeout = 30000; + VariantInit( &request->data ); + request->state = REQUEST_STATE_INITIALIZED; +} + +static HRESULT WINAPI winhttp_request_Open( + IWinHttpRequest *iface, + BSTR method, + BSTR url, + VARIANT async ) +{ + static const WCHAR typeW[] = {'*','/','*',0}; + static const WCHAR *acceptW[] = {typeW, NULL}; + static const WCHAR httpsW[] = {'h','t','t','p','s'}; + static const WCHAR user_agentW[] = { + 'M','o','z','i','l','l','a','/','4','.','0',' ','(','c','o','m','p','a','t','i','b','l','e',';',' ', + 'W','i','n','3','2',';',' ','W','i','n','H','t','t','p','.','W','i','n','H','t','t','p', + 'R','e','q','u','e','s','t','.','5',')',0}; + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HINTERNET hsession = NULL, hconnect = NULL, hrequest; + URL_COMPONENTS uc; + WCHAR *hostname, *path = NULL, *verb = NULL; + DWORD err = ERROR_OUTOFMEMORY, len, flags = 0, request_flags = 0; + + TRACE("%p, %s, %s, %s\n", request, debugstr_w(method), debugstr_w(url), + debugstr_variant(&async)); + + if (!method || !url) return E_INVALIDARG; + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + uc.dwSchemeLength = ~0u; + uc.dwHostNameLength = ~0u; + uc.dwUrlPathLength = ~0u; + uc.dwExtraInfoLength = ~0u; + if (!WinHttpCrackUrl( url, 0, 0, &uc )) return HRESULT_FROM_WIN32( get_last_error() ); + + EnterCriticalSection( &request->cs ); + if (request->state != REQUEST_STATE_INITIALIZED) + { + cancel_request( request ); + free_request( request ); + initialize_request( request ); + } + if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) goto error; + memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) ); + hostname[uc.dwHostNameLength] = 0; + + if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) ))) goto error; + memcpy( path, uc.lpszUrlPath, (uc.dwUrlPathLength + uc.dwExtraInfoLength) * sizeof(WCHAR) ); + path[uc.dwUrlPathLength + uc.dwExtraInfoLength] = 0; + + if (!(verb = strdupW( method ))) goto error; + if (V_BOOL( &async )) flags |= WINHTTP_FLAG_ASYNC; + if (!(hsession = WinHttpOpen( user_agentW, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, flags ))) + { + err = get_last_error(); + goto error; + } + if (!(hconnect = WinHttpConnect( hsession, hostname, uc.nPort, 0 ))) + { + err = get_last_error(); + goto error; + } + len = sizeof(httpsW) / sizeof(WCHAR); + if (uc.dwSchemeLength == len && !memcmp( uc.lpszScheme, httpsW, len * sizeof(WCHAR) )) + { + request_flags |= WINHTTP_FLAG_SECURE; + } + if (!(hrequest = WinHttpOpenRequest( hconnect, method, path, NULL, NULL, acceptW, request_flags ))) + { + err = get_last_error(); + goto error; + } + if (flags & WINHTTP_FLAG_ASYNC) + { + request->wait = CreateEventW( NULL, FALSE, FALSE, NULL ); + request->cancel = CreateEventW( NULL, FALSE, FALSE, NULL ); + WinHttpSetOption( hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &request, sizeof(request) ); + } + request->state = REQUEST_STATE_OPEN; + request->hsession = hsession; + request->hconnect = hconnect; + request->hrequest = hrequest; + request->verb = verb; + heap_free( hostname ); + heap_free( path ); + LeaveCriticalSection( &request->cs ); + return S_OK; + +error: + WinHttpCloseHandle( hconnect ); + WinHttpCloseHandle( hsession ); + heap_free( hostname ); + heap_free( path ); + heap_free( verb ); + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_SetRequestHeader( + IWinHttpRequest *iface, + BSTR header, + BSTR value ) +{ + static const WCHAR fmtW[] = {'%','s',':',' ','%','s','\r','\n',0}; + static const WCHAR emptyW[] = {0}; + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD len, err = ERROR_SUCCESS; + WCHAR *str; + + TRACE("%p, %s, %s\n", request, debugstr_w(header), debugstr_w(value)); + + if (!header) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_OPEN) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN; + goto done; + } + if (request->state >= REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND; + goto done; + } + len = strlenW( header ) + 4; + if (value) len += strlenW( value ); + if (!(str = heap_alloc( (len + 1) * sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + sprintfW( str, fmtW, header, value ? value : emptyW ); + if (!WinHttpAddRequestHeaders( request->hrequest, str, len, WINHTTP_ADDREQ_FLAG_REPLACE )) + { + err = get_last_error(); + } + heap_free( str ); + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_GetResponseHeader( + IWinHttpRequest *iface, + BSTR header, + BSTR *value ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD size, err = ERROR_SUCCESS; + + TRACE("%p, %p\n", request, header); + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + if (!header || !value) + { + err = ERROR_INVALID_PARAMETER; + goto done; + } + size = 0; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, NULL, &size, NULL )) + { + err = get_last_error(); + if (err != ERROR_INSUFFICIENT_BUFFER) goto done; + } + if (!(*value = SysAllocStringLen( NULL, size / sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + err = ERROR_SUCCESS; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CUSTOM, header, *value, &size, NULL )) + { + err = get_last_error(); + SysFreeString( *value ); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_GetAllResponseHeaders( + IWinHttpRequest *iface, + BSTR *headers ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD size, err = ERROR_SUCCESS; + + TRACE("%p, %p\n", request, headers); + + if (!headers) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + size = 0; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, NULL, &size, NULL )) + { + err = get_last_error(); + if (err != ERROR_INSUFFICIENT_BUFFER) goto done; + } + if (!(*headers = SysAllocStringLen( NULL, size / sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + err = ERROR_SUCCESS; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, *headers, &size, NULL )) + { + err = get_last_error(); + SysFreeString( *headers ); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static void CALLBACK wait_status_callback( HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buffer, DWORD size ) +{ + struct winhttp_request *request = (struct winhttp_request *)context; + + switch (status) + { + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + request->bytes_available = *(DWORD *)buffer; + request->error = ERROR_SUCCESS; + break; + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + request->bytes_read = size; + request->error = ERROR_SUCCESS; + break; + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + { + WINHTTP_ASYNC_RESULT *result = (WINHTTP_ASYNC_RESULT *)buffer; + request->error = result->dwError; + break; + } + default: break; + } + SetEvent( request->wait ); +} + +static void wait_set_status_callback( struct winhttp_request *request, DWORD status ) +{ + if (!request->wait) return; + status |= WINHTTP_CALLBACK_STATUS_REQUEST_ERROR; + WinHttpSetStatusCallback( request->hrequest, wait_status_callback, status, 0 ); +} + +static DWORD wait_for_completion( struct winhttp_request *request ) +{ + HANDLE handles[2]; + + if (!request->wait) + { + request->error = ERROR_SUCCESS; + return ERROR_SUCCESS; + } + handles[0] = request->wait; + handles[1] = request->cancel; + switch (WaitForMultipleObjects( 2, handles, FALSE, INFINITE )) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + request->error = ERROR_CANCELLED; + break; + default: + request->error = get_last_error(); + break; + } + return request->error; +} + +static HRESULT request_receive( struct winhttp_request *request ) +{ + DWORD err, size, total_bytes_read, buflen = 4096; + + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE ); + if (!WinHttpReceiveResponse( request->hrequest, NULL )) + { + return HRESULT_FROM_WIN32( get_last_error() ); + } + if ((err = wait_for_completion( request ))) return HRESULT_FROM_WIN32( err ); + if (!strcmpW( request->verb, headW )) + { + request->state = REQUEST_STATE_RESPONSE_RECEIVED; + return S_OK; + } + if (!(request->buffer = heap_alloc( buflen ))) return E_OUTOFMEMORY; + request->buffer[0] = 0; + size = total_bytes_read = 0; + do + { + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE ); + if (!WinHttpQueryDataAvailable( request->hrequest, &request->bytes_available )) + { + err = get_last_error(); + goto error; + } + if ((err = wait_for_completion( request ))) goto error; + if (!request->bytes_available) break; + size += request->bytes_available; + if (buflen < size) + { + char *tmp; + while (buflen < size) buflen *= 2; + if (!(tmp = heap_realloc( request->buffer, buflen ))) + { + err = ERROR_OUTOFMEMORY; + goto error; + } + request->buffer = tmp; + } + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_READ_COMPLETE ); + if (!WinHttpReadData( request->hrequest, request->buffer + request->offset, + request->bytes_available, &request->bytes_read )) + { + err = get_last_error(); + goto error; + } + if ((err = wait_for_completion( request ))) goto error; + total_bytes_read += request->bytes_read; + request->offset += request->bytes_read; + } while (request->bytes_read); + + request->state = REQUEST_STATE_RESPONSE_RECEIVED; + return S_OK; + +error: + heap_free( request->buffer ); + request->buffer = NULL; + return HRESULT_FROM_WIN32( err ); +} + +static DWORD request_set_parameters( struct winhttp_request *request ) +{ + if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_PROXY, &request->proxy, + sizeof(request->proxy) )) return get_last_error(); + + if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_AUTOLOGON_POLICY, &request->logon_policy, + sizeof(request->logon_policy) )) return get_last_error(); + + if (!WinHttpSetOption( request->hrequest, WINHTTP_OPTION_DISABLE_FEATURE, &request->disable_feature, + sizeof(request->disable_feature) )) return get_last_error(); + + if (!WinHttpSetTimeouts( request->hrequest, + request->resolve_timeout, + request->connect_timeout, + request->send_timeout, + request->receive_timeout )) return get_last_error(); + return ERROR_SUCCESS; +} + +static void request_set_utf8_content_type( struct winhttp_request *request ) +{ + static const WCHAR fmtW[] = {'%','s',':',' ','%','s',0}; + static const WCHAR text_plainW[] = {'t','e','x','t','/','p','l','a','i','n',0}; + static const WCHAR charset_utf8W[] = {'c','h','a','r','s','e','t','=','u','t','f','-','8',0}; + WCHAR headerW[64]; + int len; + + len = sprintfW( headerW, fmtW, attr_content_type, text_plainW ); + WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_ADD_IF_NEW ); + len = sprintfW( headerW, fmtW, attr_content_type, charset_utf8W ); + WinHttpAddRequestHeaders( request->hrequest, headerW, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON ); +} + +static HRESULT request_send( struct winhttp_request *request ) +{ + SAFEARRAY *sa = NULL; + VARIANT data; + char *ptr = NULL; + LONG size = 0; + HRESULT hr; + BOOL ret; + DWORD err; + + if ((err = request_set_parameters( request ))) return HRESULT_FROM_WIN32( err ); + if (strcmpW( request->verb, getW )) + { + VariantInit( &data ); + if (V_VT( &request->data ) == VT_BSTR) + { + UINT i, cp = CP_ACP; + const WCHAR *str = V_BSTR( &request->data ); + int len = strlenW( str ); + + for (i = 0; i < len; i++) + { + if (str[i] > 127) + { + cp = CP_UTF8; + break; + } + } + size = WideCharToMultiByte( cp, 0, str, len, NULL, 0, NULL, NULL ); + if (!(ptr = heap_alloc( size ))) return E_OUTOFMEMORY; + WideCharToMultiByte( cp, 0, str, len, ptr, size, NULL, NULL ); + if (cp == CP_UTF8) request_set_utf8_content_type( request ); + } + else if (VariantChangeType( &data, &request->data, 0, VT_ARRAY|VT_UI1 ) == S_OK) + { + sa = V_ARRAY( &data ); + if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) return hr; + if ((hr = SafeArrayGetUBound( sa, 1, &size ) != S_OK)) + { + SafeArrayUnaccessData( sa ); + return hr; + } + size++; + } + } + wait_set_status_callback( request, WINHTTP_CALLBACK_STATUS_REQUEST_SENT ); + if (!(ret = WinHttpSendRequest( request->hrequest, NULL, 0, ptr, size, size, 0 ))) + { + err = get_last_error(); + goto error; + } + if ((err = wait_for_completion( request ))) goto error; + if (sa) SafeArrayUnaccessData( sa ); + else heap_free( ptr ); + request->state = REQUEST_STATE_SENT; + return S_OK; + +error: + if (sa) SafeArrayUnaccessData( sa ); + else heap_free( ptr ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT request_send_and_receive( struct winhttp_request *request ) +{ + HRESULT hr = request_send( request ); + if (hr == S_OK) hr = request_receive( request ); + return hr; +} + +static DWORD CALLBACK send_and_receive_proc( void *arg ) +{ + struct winhttp_request *request = (struct winhttp_request *)arg; + return request_send_and_receive( request ); +} + +static HRESULT WINAPI winhttp_request_Send( + IWinHttpRequest *iface, + VARIANT body ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HRESULT hr; + + TRACE("%p, %s\n", request, debugstr_variant(&body)); + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_OPEN) + { + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN ); + } + if (request->state >= REQUEST_STATE_SENT) + { + LeaveCriticalSection( &request->cs ); + return S_OK; + } + VariantClear( &request->data ); + if ((hr = VariantCopyInd( &request->data, &body )) != S_OK) { + LeaveCriticalSection( &request->cs ); + return hr; + } + + if (request->wait) /* async request */ + { + if (!(request->thread = CreateThread( NULL, 0, send_and_receive_proc, request, 0, NULL ))) + { + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( get_last_error() ); + } + } + else hr = request_send_and_receive( request ); + LeaveCriticalSection( &request->cs ); + return hr; +} + +static HRESULT WINAPI winhttp_request_get_Status( + IWinHttpRequest *iface, + LONG *status ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err = ERROR_SUCCESS, flags, status_code, len = sizeof(status_code), index = 0; + + TRACE("%p, %p\n", request, status); + + if (!status) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + flags = WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER; + if (!WinHttpQueryHeaders( request->hrequest, flags, NULL, &status_code, &len, &index )) + { + err = get_last_error(); + } + *status = status_code; + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_get_StatusText( + IWinHttpRequest *iface, + BSTR *status ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err = ERROR_SUCCESS, len = 0, index = 0; + + TRACE("%p, %p\n", request, status); + + if (!status) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, NULL, &len, &index )) + { + err = get_last_error(); + if (err != ERROR_INSUFFICIENT_BUFFER) goto done; + } + if (!(*status = SysAllocStringLen( NULL, len / sizeof(WCHAR) ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + index = 0; + err = ERROR_SUCCESS; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_STATUS_TEXT, NULL, *status, &len, &index )) + { + err = get_last_error(); + SysFreeString( *status ); + } +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static DWORD request_get_codepage( struct winhttp_request *request, UINT *codepage ) +{ + static const WCHAR utf8W[] = {'u','t','f','-','8',0}; + static const WCHAR charsetW[] = {'c','h','a','r','s','e','t',0}; + WCHAR *buffer, *p; + DWORD size; + + *codepage = CP_ACP; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, NULL, &size, NULL ) && + get_last_error() == ERROR_INSUFFICIENT_BUFFER) + { + if (!(buffer = heap_alloc( size ))) return ERROR_OUTOFMEMORY; + if (!WinHttpQueryHeaders( request->hrequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, buffer, &size, NULL )) + { + return get_last_error(); + } + if ((p = strstrW( buffer, charsetW ))) + { + p += strlenW( charsetW ); + while (*p == ' ') p++; + if (*p++ == '=') + { + while (*p == ' ') p++; + if (!strcmpiW( p, utf8W )) *codepage = CP_UTF8; + } + } + heap_free( buffer ); + } + return ERROR_SUCCESS; +} + +static HRESULT WINAPI winhttp_request_get_ResponseText( + IWinHttpRequest *iface, + BSTR *body ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + UINT codepage; + DWORD err = ERROR_SUCCESS; + int len; + + TRACE("%p, %p\n", request, body); + + if (!body) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (request->state < REQUEST_STATE_SENT) + { + err = ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND; + goto done; + } + if ((err = request_get_codepage( request, &codepage ))) goto done; + len = MultiByteToWideChar( codepage, 0, request->buffer, request->offset, NULL, 0 ); + if (!(*body = SysAllocStringLen( NULL, len ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + MultiByteToWideChar( codepage, 0, request->buffer, request->offset, *body, len ); + (*body)[len] = 0; + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_get_ResponseBody( + IWinHttpRequest *iface, + VARIANT *body ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + SAFEARRAY *sa; + HRESULT hr; + DWORD err = ERROR_SUCCESS; + char *ptr; + + TRACE("%p, %p\n", request, body); + + if (!body) return E_INVALIDARG; + + EnterCriticalSection( &request->cs ); + if (!(sa = SafeArrayCreateVector( VT_UI1, 0, request->offset ))) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + if ((hr = SafeArrayAccessData( sa, (void **)&ptr )) != S_OK) + { + SafeArrayDestroy( sa ); + LeaveCriticalSection( &request->cs ); + return hr; + } + memcpy( ptr, request->buffer, request->offset ); + if ((hr = SafeArrayUnaccessData( sa )) != S_OK) + { + SafeArrayDestroy( sa ); + LeaveCriticalSection( &request->cs ); + return hr; + } + V_VT( body ) = VT_ARRAY|VT_UI1; + V_ARRAY( body ) = sa; + +done: + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_get_ResponseStream( + IWinHttpRequest *iface, + VARIANT *body ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI winhttp_request_get_Option( + IWinHttpRequest *iface, + WinHttpRequestOption option, + VARIANT *value ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI winhttp_request_put_Option( + IWinHttpRequest *iface, + WinHttpRequestOption option, + VARIANT value ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HRESULT hr = S_OK; + + TRACE("%p, %u, %s\n", request, option, debugstr_variant(&value)); + + EnterCriticalSection( &request->cs ); + switch (option) + { + case WinHttpRequestOption_EnableRedirects: + { + if (V_BOOL( &value )) + request->disable_feature &= ~WINHTTP_DISABLE_REDIRECTS; + else + request->disable_feature |= WINHTTP_DISABLE_REDIRECTS; + break; + } + default: + FIXME("unimplemented option %u\n", option); + hr = E_NOTIMPL; + break; + } + LeaveCriticalSection( &request->cs ); + return hr; +} + +/* critical section must be held */ +static DWORD wait_for_response( struct winhttp_request *request, DWORD timeout ) +{ + HANDLE thread = request->thread; + DWORD err, ret; + + LeaveCriticalSection( &request->cs ); + while ((err = MsgWaitForMultipleObjects( 1, &thread, FALSE, timeout, QS_ALLINPUT )) == WAIT_OBJECT_0 + 1) + { + MSG msg; + while (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )) + { + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } + } + switch (err) + { + case WAIT_OBJECT_0: + ret = ERROR_SUCCESS; + break; + case WAIT_TIMEOUT: + ret = ERROR_TIMEOUT; + break; + case WAIT_FAILED: + default: + ret = get_last_error(); + break; + } + EnterCriticalSection( &request->cs ); + return ret; +} + +static HRESULT WINAPI winhttp_request_WaitForResponse( + IWinHttpRequest *iface, + VARIANT timeout, + VARIANT_BOOL *succeeded ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + DWORD err, msecs = (V_I4(&timeout) == -1) ? INFINITE : V_I4(&timeout) * 1000; + + TRACE("%p, %s, %p\n", request, debugstr_variant(&timeout), succeeded); + + EnterCriticalSection( &request->cs ); + if (!request->thread) + { + LeaveCriticalSection( &request->cs ); + return S_OK; + } + if (request->state >= REQUEST_STATE_RESPONSE_RECEIVED) + { + LeaveCriticalSection( &request->cs ); + return S_OK; + } + switch ((err = wait_for_response( request, msecs ))) + { + case ERROR_TIMEOUT: + if (succeeded) *succeeded = VARIANT_FALSE; + err = ERROR_SUCCESS; + break; + + case ERROR_SUCCESS: + if (succeeded) *succeeded = VARIANT_TRUE; + break; + + default: break; + } + LeaveCriticalSection( &request->cs ); + return HRESULT_FROM_WIN32( err ); +} + +static HRESULT WINAPI winhttp_request_Abort( + IWinHttpRequest *iface ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p\n", request); + + EnterCriticalSection( &request->cs ); + cancel_request( request ); + LeaveCriticalSection( &request->cs ); + return S_OK; +} + +static HRESULT WINAPI winhttp_request_SetTimeouts( + IWinHttpRequest *iface, + LONG resolve_timeout, + LONG connect_timeout, + LONG send_timeout, + LONG receive_timeout ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + + TRACE("%p, %d, %d, %d, %d\n", request, resolve_timeout, connect_timeout, send_timeout, receive_timeout); + + EnterCriticalSection( &request->cs ); + request->resolve_timeout = resolve_timeout; + request->connect_timeout = connect_timeout; + request->send_timeout = send_timeout; + request->receive_timeout = receive_timeout; + LeaveCriticalSection( &request->cs ); + return S_OK; +} + +static HRESULT WINAPI winhttp_request_SetClientCertificate( + IWinHttpRequest *iface, + BSTR certificate ) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI winhttp_request_SetAutoLogonPolicy( + IWinHttpRequest *iface, + WinHttpRequestAutoLogonPolicy policy ) +{ + struct winhttp_request *request = impl_from_IWinHttpRequest( iface ); + HRESULT hr = S_OK; + + TRACE("%p, %u\n", request, policy ); + + EnterCriticalSection( &request->cs ); + switch (policy) + { + case AutoLogonPolicy_Always: + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; + break; + case AutoLogonPolicy_OnlyIfBypassProxy: + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_MEDIUM; + break; + case AutoLogonPolicy_Never: + request->logon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH; + break; + default: hr = E_INVALIDARG; + break; + } + LeaveCriticalSection( &request->cs ); + return hr; +} + +static const struct IWinHttpRequestVtbl winhttp_request_vtbl = +{ + winhttp_request_QueryInterface, + winhttp_request_AddRef, + winhttp_request_Release, + winhttp_request_GetTypeInfoCount, + winhttp_request_GetTypeInfo, + winhttp_request_GetIDsOfNames, + winhttp_request_Invoke, + winhttp_request_SetProxy, + winhttp_request_SetCredentials, + winhttp_request_Open, + winhttp_request_SetRequestHeader, + winhttp_request_GetResponseHeader, + winhttp_request_GetAllResponseHeaders, + winhttp_request_Send, + winhttp_request_get_Status, + winhttp_request_get_StatusText, + winhttp_request_get_ResponseText, + winhttp_request_get_ResponseBody, + winhttp_request_get_ResponseStream, + winhttp_request_get_Option, + winhttp_request_put_Option, + winhttp_request_WaitForResponse, + winhttp_request_Abort, + winhttp_request_SetTimeouts, + winhttp_request_SetClientCertificate, + winhttp_request_SetAutoLogonPolicy +}; + +HRESULT WinHttpRequest_create( IUnknown *unknown, void **obj ) +{ + struct winhttp_request *request; + + TRACE("%p, %p\n", unknown, obj); + + if (!(request = heap_alloc( sizeof(*request) ))) return E_OUTOFMEMORY; + request->IWinHttpRequest_iface.lpVtbl = &winhttp_request_vtbl; + request->refs = 1; + request->state = REQUEST_STATE_UNINITIALIZED; + request->proxy.lpszProxy = NULL; + request->proxy.lpszProxyBypass = NULL; + InitializeCriticalSection( &request->cs ); + request->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": winhttp_request.cs"); + + *obj = &request->IWinHttpRequest_iface; + TRACE("returning iface %p\n", *obj); + return S_OK; +} Index: dll/win32/winhttp/rsrc.rc =================================================================== --- dll/win32/winhttp/rsrc.rc (revision 0) +++ dll/win32/winhttp/rsrc.rc (working copy) @@ -0,0 +1,32 @@ +/* + * Copyright 2008 Robert Shearman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winhttp_tlb.tlb */ +1 TYPELIB winhttp_tlb.tlb + +/* @makedep: pac.js */ +pac.js 40 "pac.js" + +#define WINE_FILEDESCRIPTION_STR "Wine HTTP Library" +#define WINE_FILENAME_STR "winhttp.dll" +#define WINE_FILEVERSION_MAJOR 5 +#define WINE_FILEVERSION_MINOR 1 +#define WINE_FILEVERSION_BUILD 2600 +#define WINE_FILEVERSION_PLATFORMID 2180 + +#include "wine/wine_common_ver.rc" Index: dll/win32/winhttp/rsrc.rc =================================================================== --- dll/win32/winhttp/rsrc.rc (revision 0) +++ dll/win32/winhttp/rsrc.rc (working copy) Property changes on: dll/win32/winhttp/rsrc.rc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: dll/win32/winhttp/session.c =================================================================== --- dll/win32/winhttp/session.c (revision 56875) +++ dll/win32/winhttp/session.c (working copy) @@ -28,6 +28,10 @@ #include "winhttp.h" #include "wincrypt.h" #include "winreg.h" +#define COBJMACROS +#include "ole2.h" +#include "dispex.h" +#include "activscp.h" #include "winhttp_private.h" @@ -38,8 +42,8 @@ #define DEFAULT_SEND_TIMEOUT 30000 #define DEFAULT_RECEIVE_TIMEOUT 30000 -/* FIXME */ -#define CP_UNIXCP CP_ACP +static const WCHAR global_funcsW[] = {'g','l','o','b','a','l','_','f','u','n','c','s',0}; +static const WCHAR dns_resolveW[] = {'d','n','s','_','r','e','s','o','l','v','e',0}; void set_last_error( DWORD error ) { @@ -217,6 +221,7 @@ session->hdr.flags = flags; session->hdr.refs = 1; session->hdr.redirect_policy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP; + list_init( &session->hdr.children ); session->resolve_timeout = DEFAULT_RESOLVE_TIMEOUT; session->connect_timeout = DEFAULT_CONNECT_TIMEOUT; session->send_timeout = DEFAULT_SEND_TIMEOUT; @@ -426,6 +431,7 @@ session->proxy_server, colon - session->proxy_server - 1 )) { heap_free( connect->servername ); + connect->resolved = FALSE; if (!(connect->servername = heap_alloc( (colon - session->proxy_server + 1) * sizeof(WCHAR) ))) { @@ -447,6 +453,7 @@ session->proxy_server )) { heap_free( connect->servername ); + connect->resolved = FALSE; if (!(connect->servername = strdupW( session->proxy_server ))) { ret = FALSE; @@ -459,6 +466,7 @@ else if (server) { heap_free( connect->servername ); + connect->resolved = FALSE; if (!(connect->servername = strdupW( server ))) { ret = FALSE; @@ -509,6 +517,7 @@ connect->hdr.callback = session->hdr.callback; connect->hdr.notify_mask = session->hdr.notify_mask; connect->hdr.context = session->hdr.context; + list_init( &connect->hdr.children ); addref_object( &session->hdr ); connect->session = session; @@ -516,10 +525,8 @@ if (!(connect->hostname = strdupW( server ))) goto end; connect->hostport = port; + if (!set_server_for_hostname( connect, server, port )) goto end; - if (!set_server_for_hostname( connect, server, port )) - goto end; - if (!(hconnect = alloc_handle( &connect->hdr ))) goto end; connect->hdr.handle = hconnect; @@ -527,7 +534,7 @@ end: release_object( &connect->hdr ); - + release_object( &session->hdr ); TRACE("returning %p\n", hconnect); return hconnect; } @@ -538,7 +545,7 @@ static void request_destroy( object_header_t *hdr ) { request_t *request = (request_t *)hdr; - DWORD i; + unsigned int i; TRACE("%p\n", request); @@ -555,6 +562,8 @@ heap_free( request->headers[i].value ); } heap_free( request->headers ); + for (i = 0; i < request->num_accept_types; i++) heap_free( request->accept_types[i] ); + heap_free( request->accept_types ); heap_free( request ); } @@ -564,7 +573,7 @@ if (str) len = strlenW( str ); if (buffer && *buflen > len) { - memcpy( buffer, str, len * sizeof(WCHAR) ); + if (str) memcpy( buffer, str, len * sizeof(WCHAR) ); buffer[len] = 0; } *buflen = len * sizeof(WCHAR); @@ -870,6 +879,39 @@ request_set_option }; +static BOOL store_accept_types( request_t *request, const WCHAR **accept_types ) +{ + const WCHAR **types = accept_types; + int i; + + if (!types) return TRUE; + while (*types) + { + request->num_accept_types++; + types++; + } + if (!request->num_accept_types) return TRUE; + if (!(request->accept_types = heap_alloc( request->num_accept_types * sizeof(WCHAR *)))) + { + request->num_accept_types = 0; + return FALSE; + } + types = accept_types; + for (i = 0; i < request->num_accept_types; i++) + { + if (!(request->accept_types[i] = strdupW( *types ))) + { + for (; i >= 0; i--) heap_free( request->accept_types[i] ); + heap_free( request->accept_types ); + request->accept_types = NULL; + request->num_accept_types = 0; + return FALSE; + } + types++; + } + return TRUE; +} + /*********************************************************************** * WinHttpOpenRequest (winhttp.@) */ @@ -883,6 +925,14 @@ TRACE("%p, %s, %s, %s, %s, %p, 0x%08x\n", hconnect, debugstr_w(verb), debugstr_w(object), debugstr_w(version), debugstr_w(referrer), types, flags); + if(types && TRACE_ON(winhttp)) { + const WCHAR **iter; + + TRACE("accept types:\n"); + for(iter = types; *iter; iter++) + TRACE(" %s\n", debugstr_w(*iter)); + } + if (!(connect = (connect_t *)grab_object( hconnect ))) { set_last_error( ERROR_INVALID_HANDLE ); @@ -906,6 +956,7 @@ request->hdr.callback = connect->hdr.callback; request->hdr.notify_mask = connect->hdr.notify_mask; request->hdr.context = connect->hdr.context; + list_init( &request->hdr.children ); addref_object( &connect->hdr ); request->connect = connect; @@ -937,6 +988,7 @@ if (!version || !version[0]) version = http1_1; if (!(request->version = strdupW( version ))) goto end; + if (!(store_accept_types( request, types ))) goto end; if (!(hrequest = alloc_handle( &request->hdr ))) goto end; request->hdr.handle = hrequest; @@ -945,7 +997,7 @@ end: release_object( &request->hdr ); - + release_object( &connect->hdr ); TRACE("returning %p\n", hrequest); return hrequest; } @@ -1087,15 +1139,119 @@ return ret; } +static char *get_computer_name( COMPUTER_NAME_FORMAT format ) +{ + char *ret; + DWORD size = 0; + + GetComputerNameExA( format, NULL, &size ); + if (GetLastError() != ERROR_MORE_DATA) return NULL; + if (!(ret = heap_alloc( size ))) return NULL; + if (!GetComputerNameExA( format, ret, &size )) + { + heap_free( ret ); + return NULL; + } + return ret; +} + +static BOOL is_domain_suffix( const char *domain, const char *suffix ) +{ + int len_domain = strlen( domain ), len_suffix = strlen( suffix ); + + if (len_suffix > len_domain) return FALSE; + if (!strcasecmp( domain + len_domain - len_suffix, suffix )) return TRUE; + return FALSE; +} + +static void printf_addr( const WCHAR *fmt, WCHAR *buf, struct sockaddr_in *addr ) +{ + sprintfW( buf, fmt, + (unsigned int)(ntohl( addr->sin_addr.s_addr ) >> 24 & 0xff), + (unsigned int)(ntohl( addr->sin_addr.s_addr ) >> 16 & 0xff), + (unsigned int)(ntohl( addr->sin_addr.s_addr ) >> 8 & 0xff), + (unsigned int)(ntohl( addr->sin_addr.s_addr ) & 0xff) ); +} + +static WCHAR *build_wpad_url( const struct addrinfo *ai ) +{ + static const WCHAR fmtW[] = + {'h','t','t','p',':','/','/','%','u','.','%','u','.','%','u','.','%','u', + '/','w','p','a','d','.','d','a','t',0}; + WCHAR *ret; + + while (ai && ai->ai_family != AF_INET) ai = ai->ai_next; + if (!ai) return NULL; + + if (!(ret = GlobalAlloc( 0, sizeof(fmtW) + 12 * sizeof(WCHAR) ))) return NULL; + printf_addr( fmtW, ret, (struct sockaddr_in *)ai->ai_addr ); + return ret; +} + /*********************************************************************** * WinHttpDetectAutoProxyConfigUrl (winhttp.@) */ BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, LPWSTR *url ) { - FIXME("0x%08x, %p\n", flags, url); + BOOL ret = FALSE; - set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); - return FALSE; + TRACE("0x%08x, %p\n", flags, url); + + if (!flags || !url) + { + set_last_error( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (flags & WINHTTP_AUTO_DETECT_TYPE_DHCP) FIXME("discovery via DHCP not supported\n"); + if (flags & WINHTTP_AUTO_DETECT_TYPE_DNS_A) + { +#ifdef HAVE_GETADDRINFO + char *fqdn, *domain, *p; + + if (!(fqdn = get_computer_name( ComputerNamePhysicalDnsFullyQualified ))) return FALSE; + if (!(domain = get_computer_name( ComputerNamePhysicalDnsDomain ))) + { + heap_free( fqdn ); + return FALSE; + } + p = fqdn; + while ((p = strchr( p, '.' )) && is_domain_suffix( p + 1, domain )) + { + struct addrinfo *ai; + char *name; + int res; + + if (!(name = heap_alloc( sizeof("wpad") + strlen(p) ))) + { + heap_free( fqdn ); + heap_free( domain ); + return FALSE; + } + strcpy( name, "wpad" ); + strcat( name, p ); + res = getaddrinfo( name, NULL, NULL, &ai ); + heap_free( name ); + if (!res) + { + *url = build_wpad_url( ai ); + freeaddrinfo( ai ); + if (*url) + { + TRACE("returning %s\n", debugstr_w(*url)); + ret = TRUE; + break; + } + } + p++; + } + heap_free( domain ); + heap_free( fqdn ); +#else + FIXME("getaddrinfo not found at build time\n"); +#endif + } + if (!ret) set_last_error( ERROR_WINHTTP_AUTODETECTION_FAILED ); + return ret; } static const WCHAR Connections[] = { @@ -1107,15 +1263,18 @@ 'C','o','n','n','e','c','t','i','o','n','s',0 }; static const WCHAR WinHttpSettings[] = { 'W','i','n','H','t','t','p','S','e','t','t','i','n','g','s',0 }; -static const DWORD WINHTTPSETTINGS_MAGIC = 0x18; -static const DWORD WINHTTP_PROXY_TYPE_DIRECT = 1; -static const DWORD WINHTTP_PROXY_TYPE_PROXY = 2; +static const DWORD WINHTTP_SETTINGS_MAGIC = 0x18; +static const DWORD WININET_SETTINGS_MAGIC = 0x46; +static const DWORD PROXY_TYPE_DIRECT = 1; +static const DWORD PROXY_TYPE_PROXY = 2; +static const DWORD PROXY_USE_PAC_SCRIPT = 4; +static const DWORD PROXY_AUTODETECT_SETTINGS = 8; -struct winhttp_settings_header +struct connection_settings_header { DWORD magic; DWORD unknown; /* always zero? */ - DWORD flags; /* one of WINHTTP_PROXY_TYPE_* */ + DWORD flags; /* one or more of PROXY_* */ }; static inline void copy_char_to_wchar_sz(const BYTE *src, DWORD len, WCHAR *dst) @@ -1146,22 +1305,22 @@ l = RegQueryValueExW( key, WinHttpSettings, NULL, &type, NULL, &size ); if (!l && type == REG_BINARY && - size >= sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD)) + size >= sizeof(struct connection_settings_header) + 2 * sizeof(DWORD)) { BYTE *buf = heap_alloc( size ); if (buf) { - struct winhttp_settings_header *hdr = - (struct winhttp_settings_header *)buf; + struct connection_settings_header *hdr = + (struct connection_settings_header *)buf; DWORD *len = (DWORD *)(hdr + 1); l = RegQueryValueExW( key, WinHttpSettings, NULL, NULL, buf, &size ); - if (!l && hdr->magic == WINHTTPSETTINGS_MAGIC && + if (!l && hdr->magic == WINHTTP_SETTINGS_MAGIC && hdr->unknown == 0) { - if (hdr->flags & WINHTTP_PROXY_TYPE_PROXY) + if (hdr->flags & PROXY_TYPE_PROXY) { BOOL sane = FALSE; LPWSTR proxy = NULL; @@ -1267,6 +1426,13 @@ */ BOOL WINAPI WinHttpGetIEProxyConfigForCurrentUser( WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *config ) { + static const WCHAR settingsW[] = + {'D','e','f','a','u','l','t','C','o','n','n','e','c','t','i','o','n','S','e','t','t','i','n','g','s',0}; + HKEY hkey = NULL; + struct connection_settings_header *hdr = NULL; + DWORD type, offset, len, size = 0; + BOOL ret = FALSE; + TRACE("%p\n", config); if (!config) @@ -1274,28 +1440,597 @@ set_last_error( ERROR_INVALID_PARAMETER ); return FALSE; } + memset( config, 0, sizeof(*config) ); + config->fAutoDetect = TRUE; - /* FIXME: read from HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings */ + if (RegOpenKeyExW( HKEY_CURRENT_USER, Connections, 0, KEY_READ, &hkey ) || + RegQueryValueExW( hkey, settingsW, NULL, &type, NULL, &size ) || + type != REG_BINARY || size < sizeof(struct connection_settings_header)) + { + ret = TRUE; + goto done; + } + if (!(hdr = heap_alloc( size ))) goto done; + if (RegQueryValueExW( hkey, settingsW, NULL, &type, (BYTE *)hdr, &size ) || + hdr->magic != WININET_SETTINGS_MAGIC) + { + ret = TRUE; + goto done; + } - FIXME("returning no proxy used\n"); - config->fAutoDetect = FALSE; - config->lpszAutoConfigUrl = NULL; - config->lpszProxy = NULL; - config->lpszProxyBypass = NULL; + config->fAutoDetect = (hdr->flags & PROXY_AUTODETECT_SETTINGS) != 0; + offset = sizeof(*hdr); + if (offset + sizeof(DWORD) > size) goto done; + len = *(DWORD *)((char *)hdr + offset); + offset += sizeof(DWORD); + if (len && hdr->flags & PROXY_TYPE_PROXY) + { + if (!(config->lpszProxy = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done; + copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszProxy ); + } + offset += len; + if (offset + sizeof(DWORD) > size) goto done; + len = *(DWORD *)((char *)hdr + offset); + offset += sizeof(DWORD); + if (len && (hdr->flags & PROXY_TYPE_PROXY)) + { + if (!(config->lpszProxyBypass = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done; + copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszProxyBypass ); + } + offset += len; + if (offset + sizeof(DWORD) > size) goto done; + len = *(DWORD *)((char *)hdr + offset); + offset += sizeof(DWORD); + if (len && (hdr->flags & PROXY_USE_PAC_SCRIPT)) + { + if (!(config->lpszAutoConfigUrl = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) goto done; + copy_char_to_wchar_sz( (const BYTE *)hdr + offset , len, config->lpszAutoConfigUrl ); + } + ret = TRUE; +done: + RegCloseKey( hkey ); + heap_free( hdr ); + if (!ret) + { + heap_free( config->lpszAutoConfigUrl ); + config->lpszAutoConfigUrl = NULL; + heap_free( config->lpszProxy ); + config->lpszProxy = NULL; + heap_free( config->lpszProxyBypass ); + config->lpszProxyBypass = NULL; + } + return ret; +} + +static HRESULT WINAPI dispex_QueryInterface( + IDispatchEx *iface, REFIID riid, void **ppv ) +{ + *ppv = NULL; + + if (IsEqualGUID( riid, &IID_IUnknown ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IDispatchEx )) + *ppv = iface; + else + return E_NOINTERFACE; + + return S_OK; +} + +static ULONG WINAPI dispex_AddRef( + IDispatchEx *iface ) +{ + return 2; +} + +static ULONG WINAPI dispex_Release( + IDispatchEx *iface ) +{ + return 1; +} + +static HRESULT WINAPI dispex_GetTypeInfoCount( + IDispatchEx *iface, UINT *info ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetTypeInfo( + IDispatchEx *iface, UINT info, LCID lcid, ITypeInfo **type_info ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetIDsOfNames( + IDispatchEx *iface, REFIID riid, LPOLESTR *names, UINT count, LCID lcid, DISPID *id ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_Invoke( + IDispatchEx *iface, DISPID member, REFIID riid, LCID lcid, WORD flags, + DISPPARAMS *params, VARIANT *result, EXCEPINFO *excep, UINT *err ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_DeleteMemberByName( + IDispatchEx *iface, BSTR name, DWORD flags ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_DeleteMemberByDispID( + IDispatchEx *iface, DISPID id ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetMemberProperties( + IDispatchEx *iface, DISPID id, DWORD flags_fetch, DWORD *flags ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetMemberName( + IDispatchEx *iface, DISPID id, BSTR *name ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetNextDispID( + IDispatchEx *iface, DWORD flags, DISPID id, DISPID *next ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI dispex_GetNameSpaceParent( + IDispatchEx *iface, IUnknown **unk ) +{ + return E_NOTIMPL; +} + +#define DISPID_GLOBAL_DNSRESOLVE 0x1000 + +static HRESULT WINAPI dispex_GetDispID( + IDispatchEx *iface, BSTR name, DWORD flags, DISPID *id ) +{ + if (!strcmpW( name, dns_resolveW )) + { + *id = DISPID_GLOBAL_DNSRESOLVE; + return S_OK; + } + return DISP_E_UNKNOWNNAME; +} + +static HRESULT dns_resolve( const WCHAR *hostname, VARIANT *result ) +{ +#ifdef HAVE_GETADDRINFO + static const WCHAR fmtW[] = {'%','u','.','%','u','.','%','u','.','%','u',0}; + WCHAR addr[16]; + struct addrinfo *ai, *elem; + char *hostnameA; + int res; + + if (hostname[0]) + hostnameA = strdupWA( hostname ); + else + hostnameA = get_computer_name( ComputerNamePhysicalDnsFullyQualified ); + + if (!hostnameA) return E_OUTOFMEMORY; + res = getaddrinfo( hostnameA, NULL, NULL, &ai ); + heap_free( hostnameA ); + if (res) return S_FALSE; + + elem = ai; + while (elem && elem->ai_family != AF_INET) elem = elem->ai_next; + if (!elem) + { + freeaddrinfo( ai ); + return S_FALSE; + } + printf_addr( fmtW, addr, (struct sockaddr_in *)elem->ai_addr ); + freeaddrinfo( ai ); + V_VT( result ) = VT_BSTR; + V_BSTR( result ) = SysAllocString( addr ); + return S_OK; +#else + FIXME("getaddrinfo not found at build time\n"); + return S_FALSE; +#endif +} + +static HRESULT WINAPI dispex_InvokeEx( + IDispatchEx *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params, + VARIANT *result, EXCEPINFO *exep, IServiceProvider *caller ) +{ + if (id == DISPID_GLOBAL_DNSRESOLVE) + { + if (params->cArgs != 1) return DISP_E_BADPARAMCOUNT; + if (V_VT(¶ms->rgvarg[0]) != VT_BSTR) return DISP_E_BADVARTYPE; + return dns_resolve( V_BSTR(¶ms->rgvarg[0]), result ); + } + return DISP_E_MEMBERNOTFOUND; +} + +static const IDispatchExVtbl dispex_vtbl = +{ + dispex_QueryInterface, + dispex_AddRef, + dispex_Release, + dispex_GetTypeInfoCount, + dispex_GetTypeInfo, + dispex_GetIDsOfNames, + dispex_Invoke, + dispex_GetDispID, + dispex_InvokeEx, + dispex_DeleteMemberByName, + dispex_DeleteMemberByDispID, + dispex_GetMemberProperties, + dispex_GetMemberName, + dispex_GetNextDispID, + dispex_GetNameSpaceParent +}; + +static IDispatchEx global_dispex = { &dispex_vtbl }; + +static HRESULT WINAPI site_QueryInterface( + IActiveScriptSite *iface, REFIID riid, void **ppv ) +{ + *ppv = NULL; + + if (IsEqualGUID( &IID_IUnknown, riid )) + *ppv = iface; + else if (IsEqualGUID( &IID_IActiveScriptSite, riid )) + *ppv = iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef( (IUnknown *)*ppv ); + return S_OK; +} + +static ULONG WINAPI site_AddRef( + IActiveScriptSite *iface ) +{ + return 2; +} + +static ULONG WINAPI site_Release( + IActiveScriptSite *iface ) +{ + return 1; +} + +static HRESULT WINAPI site_GetLCID( + IActiveScriptSite *iface, LCID *lcid ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI site_GetItemInfo( + IActiveScriptSite *iface, LPCOLESTR name, DWORD mask, + IUnknown **item, ITypeInfo **type_info ) +{ + if (!strcmpW( name, global_funcsW ) && mask == SCRIPTINFO_IUNKNOWN) + { + *item = (IUnknown *)&global_dispex; + return S_OK; + } + return E_NOTIMPL; +} + +static HRESULT WINAPI site_GetDocVersionString( + IActiveScriptSite *iface, BSTR *version ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI site_OnScriptTerminate( + IActiveScriptSite *iface, const VARIANT *result, const EXCEPINFO *info ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI site_OnStateChange( + IActiveScriptSite *iface, SCRIPTSTATE state ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI site_OnScriptError( + IActiveScriptSite *iface, IActiveScriptError *error ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI site_OnEnterScript( + IActiveScriptSite *iface ) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI site_OnLeaveScript( + IActiveScriptSite *iface ) +{ + return E_NOTIMPL; +} + +static const IActiveScriptSiteVtbl site_vtbl = +{ + site_QueryInterface, + site_AddRef, + site_Release, + site_GetLCID, + site_GetItemInfo, + site_GetDocVersionString, + site_OnScriptTerminate, + site_OnStateChange, + site_OnScriptError, + site_OnEnterScript, + site_OnLeaveScript +}; + +static IActiveScriptSite script_site = { &site_vtbl }; + +static BOOL parse_script_result( VARIANT result, WINHTTP_PROXY_INFO *info ) +{ + static const WCHAR proxyW[] = {'P','R','O','X','Y'}; + const WCHAR *p; + WCHAR *q; + int len; + + info->dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; + info->lpszProxy = NULL; + info->lpszProxyBypass = NULL; + + if (V_VT( &result ) != VT_BSTR) return TRUE; + TRACE("%s\n", debugstr_w( V_BSTR( &result ) )); + + p = V_BSTR( &result ); + while (*p == ' ') p++; + len = strlenW( p ); + if (len >= 5 && !memicmpW( p, proxyW, sizeof(proxyW)/sizeof(WCHAR) )) + { + p += 5; + while (*p == ' ') p++; + if (!*p || *p == ';') return TRUE; + if (!(info->lpszProxy = q = strdupW( p ))) return FALSE; + info->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; + for (; *q; q++) + { + if (*q == ' ' || *q == ';') + { + *q = 0; + break; + } + } + } return TRUE; } +static BSTR include_pac_utils( BSTR script ) +{ + static const WCHAR pacjsW[] = {'p','a','c','.','j','s',0}; + HMODULE hmod = GetModuleHandleA( "winhttp.dll" ); + HRSRC rsrc; + DWORD size; + const char *data; + BSTR ret; + int len; + + if (!(rsrc = FindResourceW( hmod, pacjsW, (LPCWSTR)40 ))) return NULL; + size = SizeofResource( hmod, rsrc ); + data = LoadResource( hmod, rsrc ); + + len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 ); + if (!(ret = SysAllocStringLen( NULL, len + SysStringLen( script ) + 1 ))) return NULL; + MultiByteToWideChar( CP_ACP, 0, data, size, ret, len ); + ret[len] = 0; + strcatW( ret, script ); + return ret; +} + +static BOOL run_script( const BSTR script, const WCHAR *url, WINHTTP_PROXY_INFO *info ) +{ + static const WCHAR jscriptW[] = {'J','S','c','r','i','p','t',0}; + static const WCHAR findproxyW[] = {'F','i','n','d','P','r','o','x','y','F','o','r','U','R','L',0}; + IActiveScriptParse *parser = NULL; + IActiveScript *engine = NULL; + IDispatch *dispatch = NULL; + BOOL ret = FALSE; + CLSID clsid; + DISPID dispid; + BSTR func = NULL, hostname = NULL, full_script = NULL; + URL_COMPONENTSW uc; + VARIANT args[2], result; + DISPPARAMS params; + HRESULT hr, init; + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + if (!WinHttpCrackUrl( url, 0, 0, &uc )) return FALSE; + if (!(hostname = SysAllocStringLen( NULL, uc.dwHostNameLength + 1 ))) return FALSE; + memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) ); + hostname[uc.dwHostNameLength] = 0; + + init = CoInitialize( NULL ); + hr = CLSIDFromProgID( jscriptW, &clsid ); + if (hr != S_OK) goto done; + + hr = CoCreateInstance( &clsid, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, + &IID_IActiveScript, (void **)&engine ); + if (hr != S_OK) goto done; + + hr = IActiveScript_QueryInterface( engine, &IID_IActiveScriptParse, (void **)&parser ); + if (hr != S_OK) goto done; + + hr = IActiveScriptParse64_InitNew( parser ); + if (hr != S_OK) goto done; + + hr = IActiveScript_SetScriptSite( engine, &script_site ); + if (hr != S_OK) goto done; + + hr = IActiveScript_AddNamedItem( engine, global_funcsW, SCRIPTITEM_GLOBALMEMBERS ); + if (hr != S_OK) goto done; + + if (!(full_script = include_pac_utils( script ))) goto done; + + hr = IActiveScriptParse64_ParseScriptText( parser, full_script, NULL, NULL, NULL, 0, 0, 0, NULL, NULL ); + if (hr != S_OK) goto done; + + hr = IActiveScript_SetScriptState( engine, SCRIPTSTATE_STARTED ); + if (hr != S_OK) goto done; + + hr = IActiveScript_GetScriptDispatch( engine, NULL, &dispatch ); + if (hr != S_OK) goto done; + + if (!(func = SysAllocString( findproxyW ))) goto done; + hr = IDispatch_GetIDsOfNames( dispatch, &IID_NULL, &func, 1, LOCALE_SYSTEM_DEFAULT, &dispid ); + if (hr != S_OK) goto done; + + V_VT( &args[0] ) = VT_BSTR; + V_BSTR( &args[0] ) = hostname; + V_VT( &args[1] ) = VT_BSTR; + V_BSTR( &args[1] ) = SysAllocString( url ); + + params.rgvarg = args; + params.rgdispidNamedArgs = NULL; + params.cArgs = 2; + params.cNamedArgs = 0; + hr = IDispatch_Invoke( dispatch, dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, + ¶ms, &result, NULL, NULL ); + VariantClear( &args[1] ); + if (hr != S_OK) + { + WARN("script failed 0x%08x\n", hr); + goto done; + } + ret = parse_script_result( result, info ); + +done: + SysFreeString( full_script ); + SysFreeString( hostname ); + SysFreeString( func ); + if (dispatch) IDispatch_Release( dispatch ); + if (parser) IUnknown_Release( parser ); + if (engine) IActiveScript_Release( engine ); + if (SUCCEEDED( init )) CoUninitialize(); + if (!ret) set_last_error( ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT ); + return ret; +} + +static BSTR download_script( const WCHAR *url ) +{ + static const WCHAR typeW[] = {'*','/','*',0}; + static const WCHAR *acceptW[] = {typeW, NULL}; + HINTERNET ses, con = NULL, req = NULL; + WCHAR *hostname; + URL_COMPONENTSW uc; + DWORD size = 4096, offset, to_read, bytes_read, flags = 0; + char *tmp, *buffer = NULL; + BSTR script = NULL; + int len; + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + if (!WinHttpCrackUrl( url, 0, 0, &uc )) return NULL; + if (!(hostname = heap_alloc( (uc.dwHostNameLength + 1) * sizeof(WCHAR) ))) return NULL; + memcpy( hostname, uc.lpszHostName, uc.dwHostNameLength * sizeof(WCHAR) ); + hostname[uc.dwHostNameLength] = 0; + + if (!(ses = WinHttpOpen( NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0 ))) goto done; + if (!(con = WinHttpConnect( ses, hostname, uc.nPort, 0 ))) goto done; + if (uc.nScheme == INTERNET_SCHEME_HTTPS) flags |= WINHTTP_FLAG_SECURE; + if (!(req = WinHttpOpenRequest( con, NULL, uc.lpszUrlPath, NULL, NULL, acceptW, flags ))) goto done; + if (!WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 )) goto done; + if (!(WinHttpReceiveResponse( req, 0 ))) goto done; + + if (!(buffer = heap_alloc( size ))) goto done; + to_read = size; + offset = 0; + for (;;) + { + if (!WinHttpReadData( req, buffer + offset, to_read, &bytes_read )) goto done; + if (!bytes_read) break; + to_read -= bytes_read; + offset += bytes_read; + if (!to_read) + { + to_read = size; + size *= 2; + if (!(tmp = heap_realloc( buffer, size ))) goto done; + buffer = tmp; + } + } + len = MultiByteToWideChar( CP_ACP, 0, buffer, offset, NULL, 0 ); + if (!(script = SysAllocStringLen( NULL, len ))) goto done; + MultiByteToWideChar( CP_ACP, 0, buffer, offset, script, len ); + script[len] = 0; + +done: + WinHttpCloseHandle( req ); + WinHttpCloseHandle( con ); + WinHttpCloseHandle( ses ); + heap_free( buffer ); + heap_free( hostname ); + if (!script) set_last_error( ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT ); + return script; +} + /*********************************************************************** * WinHttpGetProxyForUrl (winhttp.@) */ BOOL WINAPI WinHttpGetProxyForUrl( HINTERNET hsession, LPCWSTR url, WINHTTP_AUTOPROXY_OPTIONS *options, WINHTTP_PROXY_INFO *info ) { - FIXME("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info); + WCHAR *detected_pac_url = NULL; + const WCHAR *pac_url; + session_t *session; + BSTR script; + BOOL ret = FALSE; - set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR ); - return FALSE; + TRACE("%p, %s, %p, %p\n", hsession, debugstr_w(url), options, info); + + if (!(session = (session_t *)grab_object( hsession ))) + { + set_last_error( ERROR_INVALID_HANDLE ); + return FALSE; + } + if (session->hdr.type != WINHTTP_HANDLE_TYPE_SESSION) + { + release_object( &session->hdr ); + set_last_error( ERROR_WINHTTP_INCORRECT_HANDLE_TYPE ); + return FALSE; + } + if (!url || !options || !info || + !(options->dwFlags & (WINHTTP_AUTOPROXY_AUTO_DETECT|WINHTTP_AUTOPROXY_CONFIG_URL)) || + ((options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) && !options->dwAutoDetectFlags) || + ((options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) && + (options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL))) + { + release_object( &session->hdr ); + set_last_error( ERROR_INVALID_PARAMETER ); + return FALSE; + } + if (options->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT && + !WinHttpDetectAutoProxyConfigUrl( options->dwAutoDetectFlags, &detected_pac_url )) + { + set_last_error( ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR ); + goto done; + } + if (options->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL) pac_url = options->lpszAutoConfigUrl; + else pac_url = detected_pac_url; + + if (!(script = download_script( pac_url ))) goto done; + ret = run_script( script, url, info ); + SysFreeString( script ); + +done: + GlobalFree( detected_pac_url ); + release_object( &session->hdr ); + return ret; } /*********************************************************************** @@ -1351,7 +2086,7 @@ KEY_WRITE, NULL, &key, NULL ); if (!l) { - DWORD size = sizeof(struct winhttp_settings_header) + 2 * sizeof(DWORD); + DWORD size = sizeof(struct connection_settings_header) + 2 * sizeof(DWORD); BYTE *buf; if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) @@ -1363,17 +2098,17 @@ buf = heap_alloc( size ); if (buf) { - struct winhttp_settings_header *hdr = - (struct winhttp_settings_header *)buf; + struct connection_settings_header *hdr = + (struct connection_settings_header *)buf; DWORD *len = (DWORD *)(hdr + 1); - hdr->magic = WINHTTPSETTINGS_MAGIC; + hdr->magic = WINHTTP_SETTINGS_MAGIC; hdr->unknown = 0; if (info->dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { BYTE *dst; - hdr->flags = WINHTTP_PROXY_TYPE_PROXY; + hdr->flags = PROXY_TYPE_PROXY; *len++ = strlenW( info->lpszProxy ); for (dst = (BYTE *)len, src = info->lpszProxy; *src; src++, dst++) @@ -1391,7 +2126,7 @@ } else { - hdr->flags = WINHTTP_PROXY_TYPE_DIRECT; + hdr->flags = PROXY_TYPE_DIRECT; *len++ = 0; *len++ = 0; } @@ -1606,7 +2341,6 @@ while (*s && !isdigitW( *s )) s++; if (*s == '\0') return TRUE; time->wSecond = strtolW( s, &end, 10 ); - s = end; time->wMilliseconds = 0; return TRUE; Index: dll/win32/winhttp/url.c =================================================================== --- dll/win32/winhttp/url.c (revision 56875) +++ dll/win32/winhttp/url.c (working copy) @@ -38,7 +38,7 @@ { if (!*str) { - if (len && (flags & ICU_DECODE)) + if (len && *str_len && (flags & (ICU_DECODE|ICU_ESCAPE))) { set_last_error( ERROR_INVALID_PARAMETER ); return FALSE; @@ -61,27 +61,121 @@ return TRUE; } -static BOOL decode_url( LPCWSTR url, LPWSTR buffer, LPDWORD buflen ) +static WCHAR *decode_url( LPCWSTR url, DWORD *len ) { - HRESULT hr = UrlCanonicalizeW( url, buffer, buflen, URL_WININET_COMPATIBILITY | URL_UNESCAPE ); - if (hr == E_POINTER) set_last_error( ERROR_INSUFFICIENT_BUFFER ); - if (hr == E_INVALIDARG) set_last_error( ERROR_INVALID_PARAMETER ); - return (SUCCEEDED(hr)) ? TRUE : FALSE; + const WCHAR *p = url; + WCHAR hex[3], *q, *ret; + + if (!(ret = heap_alloc( *len * sizeof(WCHAR) ))) return NULL; + q = ret; + while (*len > 0) + { + if (p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] )) + { + hex[0] = p[1]; + hex[1] = p[2]; + hex[2] = 0; + *q++ = strtolW( hex, NULL, 16 ); + p += 3; + *len -= 3; + } + else + { + *q++ = *p++; + *len -= 1; + } + } + *len = q - ret; + return ret; } +static BOOL need_escape( WCHAR c ) +{ + if (isalnumW( c )) return FALSE; + + if (c <= 31 || c >= 127) return TRUE; + else + { + switch (c) + { + case ' ': + case '"': + case '#': + case '%': + case '<': + case '>': + case ']': + case '\\': + case '[': + case '^': + case '`': + case '{': + case '|': + case '}': + case '~': + return TRUE; + default: + return FALSE; + } + } +} + +static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len ) +{ + static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + DWORD ret = len; + unsigned int i; + WCHAR *p = dst; + + for (i = 0; i < len; i++, p++) + { + if (need_escape( src[i] )) + { + p[0] = '%'; + p[1] = hex[(src[i] >> 4) & 0xf]; + p[2] = hex[src[i] & 0xf]; + ret += 2; + p += 2; + } + else *p = src[i]; + } + dst[ret] = 0; + return ret; +} + +static WCHAR *escape_url( LPCWSTR url, DWORD *len ) +{ + WCHAR *ret; + const WCHAR *p, *q; + + if ((p = q = strrchrW( url, '/' ))) + { + while (*q) + { + if (need_escape( *q )) *len += 2; + q++; + } + } + if (!(ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL; + if (!p) strcpyW( ret, url ); + else + { + memcpy( ret, url, (p - url) * sizeof(WCHAR) ); + copy_escape( ret + (p - url), p, q - p ); + } + return ret; +} + /*********************************************************************** * WinHttpCrackUrl (winhttp.@) */ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW uc ) { BOOL ret = FALSE; - WCHAR *p, *q, *r; - WCHAR *url_decoded = NULL; + WCHAR *p, *q, *r, *url_decoded = NULL, *url_escaped = NULL; TRACE("%s, %d, %x, %p\n", debugstr_w(url), len, flags, uc); - if (flags & ICU_ESCAPE) FIXME("flag ICU_ESCAPE not supported\n"); - if (!url || !uc || uc->dwStructSize != sizeof(URL_COMPONENTS)) { set_last_error( ERROR_INVALID_PARAMETER ); @@ -89,30 +183,23 @@ } if (!len) len = strlenW( url ); - if (flags & ICU_DECODE) + if (flags & ICU_ESCAPE) { - WCHAR *url_tmp; - DWORD url_len = len + 1; - - if (!(url_tmp = HeapAlloc( GetProcessHeap(), 0, url_len * sizeof(WCHAR) ))) + if (!(url_escaped = escape_url( url, &len ))) { set_last_error( ERROR_OUTOFMEMORY ); return FALSE; } - memcpy( url_tmp, url, len * sizeof(WCHAR) ); - url_tmp[len] = 0; - if (!(url_decoded = HeapAlloc( GetProcessHeap(), 0, url_len * sizeof(WCHAR) ))) + url = url_escaped; + } + else if (flags & ICU_DECODE) + { + if (!(url_decoded = decode_url( url, &len ))) { - HeapFree( GetProcessHeap(), 0, url_tmp ); set_last_error( ERROR_OUTOFMEMORY ); return FALSE; } - if (decode_url( url_tmp, url_decoded, &url_len )) - { - len = url_len; - url = url_decoded; - } - HeapFree( GetProcessHeap(), 0, url_tmp ); + url = url_decoded; } if (!(p = strchrW( url, ':' ))) { @@ -207,7 +294,8 @@ debugstr_wn( uc->lpszExtraInfo, uc->dwExtraInfoLength )); exit: - HeapFree( GetProcessHeap(), 0, url_decoded ); + heap_free( url_decoded ); + heap_free( url_escaped ); return ret; } @@ -232,60 +320,6 @@ return FALSE; } -static BOOL need_escape( WCHAR c ) -{ - if (isalnumW( c )) return FALSE; - - if (c <= 31 || c >= 127) return TRUE; - else - { - switch (c) - { - case ' ': - case '"': - case '#': - case '%': - case '<': - case '>': - case ']': - case '\\': - case '[': - case '^': - case '`': - case '{': - case '|': - case '}': - case '~': - return TRUE; - default: - return FALSE; - } - } -} - -static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len ) -{ - static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - DWORD ret = len; - unsigned int i; - WCHAR *p = dst; - - for (i = 0; i < len; i++, p++) - { - if (need_escape( src[i] )) - { - p[0] = '%'; - p[1] = hex[(src[i] >> 4) & 0xf]; - p[2] = hex[src[i] & 0xf]; - ret += 2; - p += 2; - } - else *p = src[i]; - } - dst[ret] = 0; - return ret; -} - static DWORD comp_length( DWORD len, DWORD flags, WCHAR *comp ) { DWORD ret; Index: dll/win32/winhttp/winhttp_private.h =================================================================== --- dll/win32/winhttp/winhttp_private.h (revision 56875) +++ dll/win32/winhttp/winhttp_private.h (working copy) @@ -42,9 +42,11 @@ # define closesocket close # define ioctlsocket ioctl #endif +#include "ole2.h" static const WCHAR getW[] = {'G','E','T',0}; static const WCHAR postW[] = {'P','O','S','T',0}; +static const WCHAR headW[] = {'H','E','A','D',0}; static const WCHAR slashW[] = {'/',0}; static const WCHAR http1_0[] = {'H','T','T','P','/','1','.','0',0}; static const WCHAR http1_1[] = {'H','T','T','P','/','1','.','1',0}; @@ -118,6 +120,7 @@ INTERNET_PORT hostport; INTERNET_PORT serverport; struct sockaddr_storage sockaddr; + BOOL resolved; } connect_t; typedef struct @@ -156,6 +159,8 @@ DWORD content_read; /* bytes read so far */ header_t *headers; DWORD num_headers; + WCHAR **accept_types; + DWORD num_accept_types; } request_t; typedef struct _task_header_t task_header_t; @@ -237,6 +242,39 @@ void delete_domain( domain_t * ) DECLSPEC_HIDDEN; BOOL set_server_for_hostname( connect_t *connect, LPCWSTR server, INTERNET_PORT port ) DECLSPEC_HIDDEN; +extern HRESULT WinHttpRequest_create( IUnknown *, void ** ) DECLSPEC_HIDDEN; + +static inline const char *debugstr_variant( const VARIANT *v ) +{ + if (!v) return "(null)"; + switch (V_VT(v)) + { + case VT_EMPTY: + return "{VT_EMPTY}"; + case VT_NULL: + return "{VT_NULL}"; + case VT_I4: + return wine_dbg_sprintf( "{VT_I4: %d}", V_I4(v) ); + case VT_R8: + return wine_dbg_sprintf( "{VT_R8: %lf}", V_R8(v) ); + case VT_BSTR: + return wine_dbg_sprintf( "{VT_BSTR: %s}", debugstr_w(V_BSTR(v)) ); + case VT_DISPATCH: + return wine_dbg_sprintf( "{VT_DISPATCH: %p}", V_DISPATCH(v) ); + case VT_BOOL: + return wine_dbg_sprintf( "{VT_BOOL: %x}", V_BOOL(v) ); + case VT_UNKNOWN: + return wine_dbg_sprintf( "{VT_UNKNOWN: %p}", V_UNKNOWN(v) ); + case VT_UINT: + return wine_dbg_sprintf( "{VT_UINT: %u}", V_UINT(v) ); + case VT_BSTR|VT_BYREF: + return wine_dbg_sprintf( "{VT_BSTR|VT_BYREF: ptr %p, data %s}", + V_BSTRREF(v), V_BSTRREF(v) ? debugstr_w( *V_BSTRREF(v) ) : NULL ); + default: + return wine_dbg_sprintf( "{vt %d}", V_VT(v) ); + } +} + static inline void *heap_alloc( SIZE_T size ) { return HeapAlloc( GetProcessHeap(), 0, size ); Index: dll/win32/winhttp/winhttp_tlb.idl =================================================================== --- dll/win32/winhttp/winhttp_tlb.idl (revision 0) +++ dll/win32/winhttp/winhttp_tlb.idl (working copy) @@ -0,0 +1,21 @@ +/* + * Typelib for winhttp + * + * Copyright 2011 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "httprequest.idl" Index: dll/win32/winhttp/winhttp_tlb.idl =================================================================== --- dll/win32/winhttp/winhttp_tlb.idl (revision 0) +++ dll/win32/winhttp/winhttp_tlb.idl (working copy) Property changes on: dll/win32/winhttp/winhttp_tlb.idl ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property