wine/dlls/wininet/internet.c
Jason Kuo 00ea069777 wininet: Partially implement InternetSetFilePointer.
The function is implemented by setting the newly added contentPos
that keep track of current read position in the content, when
InternetReadFile related function is called, data will be read from
req_file->file_handle before continuing reading from http stream.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=26570
2023-02-06 10:13:38 +01:00

4849 lines
142 KiB
C

/*
* Wininet
*
* Copyright 1999 Corel Corporation
* Copyright 2002 CodeWeavers Inc.
* Copyright 2002 Jaco Greeff
* Copyright 2002 TransGaming Technologies Inc.
* Copyright 2004 Mike McCormack for CodeWeavers
*
* Ulrich Czekalla
* Aric Stewart
* David Hammerton
*
* 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 "winsock2.h"
#include "ws2ipdef.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <wchar.h>
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winuser.h"
#include "wininet.h"
#include "winnls.h"
#include "wine/debug.h"
#include "winerror.h"
#define NO_SHLWAPI_STREAM
#include "shlwapi.h"
#include "ws2tcpip.h"
#include "winternl.h"
#include "iphlpapi.h"
#include "dhcpcsdk.h"
#include "internet.h"
#include "resource.h"
WINE_DEFAULT_DEBUG_CHANNEL(wininet);
typedef struct
{
DWORD dwError;
CHAR response[MAX_REPLY_LEN];
} WITHREADERROR, *LPWITHREADERROR;
static DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
HMODULE WININET_hModule;
static CRITICAL_SECTION WININET_cs;
static CRITICAL_SECTION_DEBUG WININET_cs_debug =
{
0, 0, &WININET_cs,
{ &WININET_cs_debug.ProcessLocksList, &WININET_cs_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": WININET_cs") }
};
static CRITICAL_SECTION WININET_cs = { &WININET_cs_debug, -1, 0, 0, 0, 0 };
static object_header_t **handle_table;
static UINT_PTR next_handle;
static UINT_PTR handle_table_size;
typedef struct
{
DWORD proxyEnabled;
LPWSTR proxy;
LPWSTR proxyBypass;
LPWSTR proxyUsername;
LPWSTR proxyPassword;
} proxyinfo_t;
static ULONG max_conns = 2, max_1_0_conns = 4;
static ULONG connect_timeout = 60000;
static const WCHAR szInternetSettings[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
void *alloc_object(object_header_t *parent, const object_vtbl_t *vtbl, size_t size)
{
UINT_PTR handle = 0, num;
object_header_t *ret;
object_header_t **p;
BOOL res = TRUE;
ret = calloc(1, size);
if(!ret)
return NULL;
list_init(&ret->children);
EnterCriticalSection( &WININET_cs );
if(!handle_table_size) {
num = 16;
p = calloc(num, sizeof(handle_table[0]));
if(p) {
handle_table = p;
handle_table_size = num;
next_handle = 1;
}else {
res = FALSE;
}
}else if(next_handle == handle_table_size) {
num = handle_table_size * 2;
p = realloc(handle_table, sizeof(handle_table[0]) * num);
if(p) {
memset(p + handle_table_size, 0, sizeof(handle_table[0]) * handle_table_size);
handle_table = p;
handle_table_size = num;
}else {
res = FALSE;
}
}
if(res) {
handle = next_handle;
if(handle_table[handle])
ERR("handle isn't free but should be\n");
handle_table[handle] = ret;
ret->valid_handle = TRUE;
while(next_handle < handle_table_size && handle_table[next_handle])
next_handle++;
}
LeaveCriticalSection( &WININET_cs );
if(!res) {
free(ret);
return NULL;
}
ret->vtbl = vtbl;
ret->refs = 1;
ret->hInternet = (HINTERNET)handle;
if(parent) {
ret->lpfnStatusCB = parent->lpfnStatusCB;
ret->dwInternalFlags = parent->dwInternalFlags & INET_CALLBACKW;
}
return ret;
}
object_header_t *WININET_AddRef( object_header_t *info )
{
ULONG refs = InterlockedIncrement(&info->refs);
TRACE("%p -> refcount = %ld\n", info, refs );
return info;
}
object_header_t *get_handle_object( HINTERNET hinternet )
{
object_header_t *info = NULL;
UINT_PTR handle = (UINT_PTR) hinternet;
EnterCriticalSection( &WININET_cs );
if(handle > 0 && handle < handle_table_size && handle_table[handle] && handle_table[handle]->valid_handle)
info = WININET_AddRef(handle_table[handle]);
LeaveCriticalSection( &WININET_cs );
TRACE("handle %Id -> %p\n", handle, info);
return info;
}
static void invalidate_handle(object_header_t *info)
{
object_header_t *child, *next;
if(!info->valid_handle)
return;
info->valid_handle = FALSE;
/* Free all children as native does */
LIST_FOR_EACH_ENTRY_SAFE( child, next, &info->children, object_header_t, entry )
{
TRACE("invalidating child handle %p for parent %p\n", child->hInternet, info);
invalidate_handle( child );
}
WININET_Release(info);
}
BOOL WININET_Release( object_header_t *info )
{
ULONG refs = InterlockedDecrement(&info->refs);
TRACE( "object %p refcount = %ld\n", info, refs );
if( !refs )
{
invalidate_handle(info);
if ( info->vtbl->CloseConnection )
{
TRACE( "closing connection %p\n", info);
info->vtbl->CloseConnection( info );
}
/* Don't send a callback if this is a session handle created with InternetOpenUrl */
if ((info->htype != WH_HHTTPSESSION && info->htype != WH_HFTPSESSION)
|| !(info->dwInternalFlags & INET_OPENURL))
{
INTERNET_SendCallback(info, info->dwContext,
INTERNET_STATUS_HANDLE_CLOSING, &info->hInternet,
sizeof(HINTERNET));
}
TRACE( "destroying object %p\n", info);
if ( info->htype != WH_HINIT )
list_remove( &info->entry );
info->vtbl->Destroy( info );
if(info->hInternet) {
UINT_PTR handle = (UINT_PTR)info->hInternet;
EnterCriticalSection( &WININET_cs );
handle_table[handle] = NULL;
if(next_handle > handle)
next_handle = handle;
LeaveCriticalSection( &WININET_cs );
}
free(info);
}
return TRUE;
}
/***********************************************************************
* DllMain [Internal] Initializes the internal 'WININET.DLL'.
*
* PARAMS
* hinstDLL [I] handle to the DLL's instance
* fdwReason [I]
* lpvReserved [I] reserved, must be NULL
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
g_dwTlsErrIndex = TlsAlloc();
if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
return FALSE;
if(!init_urlcache())
{
TlsFree(g_dwTlsErrIndex);
return FALSE;
}
WININET_hModule = hinstDLL;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
{
free(TlsGetValue(g_dwTlsErrIndex));
}
break;
case DLL_PROCESS_DETACH:
if (lpvReserved) break;
collect_connections(COLLECT_CLEANUP);
NETCON_unload();
free_urlcache();
free_cookie();
if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
{
free(TlsGetValue(g_dwTlsErrIndex));
TlsFree(g_dwTlsErrIndex);
}
break;
}
return TRUE;
}
/***********************************************************************
* DllInstall (WININET.@)
*/
HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
{
FIXME("(%x %s): stub\n", bInstall, debugstr_w(cmdline));
return S_OK;
}
/***********************************************************************
* INTERNET_SaveProxySettings
*
* Stores the proxy settings given by lpwai into the registry
*
* RETURNS
* ERROR_SUCCESS if no error, or error code on fail
*/
static LONG INTERNET_SaveProxySettings( proxyinfo_t *lpwpi )
{
HKEY key;
LONG ret;
if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
return ret;
if ((ret = RegSetValueExW( key, L"ProxyEnable", 0, REG_DWORD, (BYTE*)&lpwpi->proxyEnabled, sizeof(DWORD))))
{
RegCloseKey( key );
return ret;
}
if (lpwpi->proxy)
{
if ((ret = RegSetValueExW( key, L"ProxyServer", 0, REG_SZ, (BYTE*)lpwpi->proxy, sizeof(WCHAR) * (lstrlenW(lpwpi->proxy) + 1))))
{
RegCloseKey( key );
return ret;
}
}
else
{
if ((ret = RegDeleteValueW( key, L"ProxyServer" )) && ret != ERROR_FILE_NOT_FOUND)
{
RegCloseKey( key );
return ret;
}
}
RegCloseKey(key);
return ERROR_SUCCESS;
}
/***********************************************************************
* INTERNET_FindProxyForProtocol
*
* Searches the proxy string for a proxy of the given protocol.
* Returns the found proxy, or the default proxy if none of the given
* protocol is found.
*
* PARAMETERS
* szProxy [In] proxy string to search
* proto [In] protocol to search for, e.g. "http"
* foundProxy [Out] found proxy
* foundProxyLen [In/Out] length of foundProxy buffer, in WCHARs
*
* RETURNS
* TRUE if a proxy is found, FALSE if not. If foundProxy is too short,
* *foundProxyLen is set to the required size in WCHARs, including the
* NULL terminator, and the last error is set to ERROR_INSUFFICIENT_BUFFER.
*/
WCHAR *INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto)
{
WCHAR *ret = NULL;
const WCHAR *ptr;
TRACE("(%s, %s)\n", debugstr_w(szProxy), debugstr_w(proto));
/* First, look for the specified protocol (proto=scheme://host:port) */
for (ptr = szProxy; ptr && *ptr; )
{
LPCWSTR end, equal;
if (!(end = wcschr(ptr, ' ')))
end = ptr + lstrlenW(ptr);
if ((equal = wcschr(ptr, '=')) && equal < end &&
equal - ptr == lstrlenW(proto) &&
!wcsnicmp(proto, ptr, lstrlenW(proto)))
{
ret = strndupW(equal + 1, end - equal - 1);
TRACE("found proxy for %s: %s\n", debugstr_w(proto), debugstr_w(ret));
return ret;
}
if (*end == ' ')
ptr = end + 1;
else
ptr = end;
}
/* It wasn't found: look for no protocol */
for (ptr = szProxy; ptr && *ptr; )
{
LPCWSTR end;
if (!(end = wcschr(ptr, ' ')))
end = ptr + lstrlenW(ptr);
if (!wcschr(ptr, '='))
{
ret = strndupW(ptr, end - ptr);
TRACE("found proxy for %s: %s\n", debugstr_w(proto), debugstr_w(ret));
return ret;
}
if (*end == ' ')
ptr = end + 1;
else
ptr = end;
}
return NULL;
}
/***********************************************************************
* InternetInitializeAutoProxyDll (WININET.@)
*
* Setup the internal proxy
*
* PARAMETERS
* dwReserved
*
* RETURNS
* FALSE on failure
*
*/
BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
{
FIXME("STUB\n");
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/***********************************************************************
* DetectAutoProxyUrl (WININET.@)
*
* Auto detect the proxy url
*
* RETURNS
* FALSE on failure
*
*/
BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
{
FIXME("STUB\n");
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
static void FreeProxyInfo( proxyinfo_t *lpwpi )
{
free(lpwpi->proxy);
free(lpwpi->proxyBypass);
free(lpwpi->proxyUsername);
free(lpwpi->proxyPassword);
}
static proxyinfo_t *global_proxy;
static void free_global_proxy( void )
{
EnterCriticalSection( &WININET_cs );
if (global_proxy)
{
FreeProxyInfo( global_proxy );
free( global_proxy );
}
LeaveCriticalSection( &WININET_cs );
}
static BOOL parse_proxy_url( proxyinfo_t *info, const WCHAR *url )
{
URL_COMPONENTSW uc = {sizeof(uc)};
uc.dwHostNameLength = 1;
uc.dwUserNameLength = 1;
uc.dwPasswordLength = 1;
if (!InternetCrackUrlW( url, 0, 0, &uc )) return FALSE;
if (!uc.dwHostNameLength)
{
if (!(info->proxy = wcsdup( url ))) return FALSE;
info->proxyUsername = NULL;
info->proxyPassword = NULL;
return TRUE;
}
if (!(info->proxy = malloc( (uc.dwHostNameLength + 12) * sizeof(WCHAR) ))) return FALSE;
swprintf( info->proxy, uc.dwHostNameLength + 12, L"%.*s:%u", uc.dwHostNameLength, uc.lpszHostName, uc.nPort );
if (!uc.dwUserNameLength) info->proxyUsername = NULL;
else if (!(info->proxyUsername = strndupW( uc.lpszUserName, uc.dwUserNameLength )))
{
free( info->proxy );
return FALSE;
}
if (!uc.dwPasswordLength) info->proxyPassword = NULL;
else if (!(info->proxyPassword = strndupW( uc.lpszPassword, uc.dwPasswordLength )))
{
free( info->proxyUsername );
free( info->proxy );
return FALSE;
}
return TRUE;
}
/***********************************************************************
* INTERNET_LoadProxySettings
*
* Loads proxy information from process-wide global settings, the registry,
* or the environment into lpwpi.
*
* The caller should call FreeProxyInfo when done with lpwpi.
*
* FIXME:
* The proxy may be specified in the form 'http=proxy.my.org'
* Presumably that means there can be ftp=ftpproxy.my.org too.
*/
static LONG INTERNET_LoadProxySettings( proxyinfo_t *lpwpi )
{
HKEY key;
DWORD type, len;
const WCHAR *envproxy;
LONG ret;
memset( lpwpi, 0, sizeof(*lpwpi) );
EnterCriticalSection( &WININET_cs );
if (global_proxy)
{
lpwpi->proxyEnabled = global_proxy->proxyEnabled;
lpwpi->proxy = wcsdup( global_proxy->proxy );
lpwpi->proxyBypass = wcsdup( global_proxy->proxyBypass );
}
LeaveCriticalSection( &WININET_cs );
if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
{
FreeProxyInfo( lpwpi );
return ret;
}
len = sizeof(DWORD);
if (RegQueryValueExW( key, L"ProxyEnable", NULL, &type, (BYTE *)&lpwpi->proxyEnabled, &len ) || type != REG_DWORD)
{
lpwpi->proxyEnabled = 0;
if((ret = RegSetValueExW( key, L"ProxyEnable", 0, REG_DWORD, (BYTE *)&lpwpi->proxyEnabled, sizeof(DWORD) )))
{
FreeProxyInfo( lpwpi );
RegCloseKey( key );
return ret;
}
}
if (!(envproxy = _wgetenv( L"http_proxy" )) || lpwpi->proxyEnabled)
{
/* figure out how much memory the proxy setting takes */
if (!RegQueryValueExW( key, L"ProxyServer", NULL, &type, NULL, &len ) && len && (type == REG_SZ))
{
LPWSTR szProxy, p;
if (!(szProxy = malloc( len )))
{
RegCloseKey( key );
FreeProxyInfo( lpwpi );
return ERROR_OUTOFMEMORY;
}
RegQueryValueExW( key, L"ProxyServer", NULL, &type, (BYTE*)szProxy, &len );
/* find the http proxy, and strip away everything else */
p = wcsstr( szProxy, L"http=" );
if (p)
{
p += lstrlenW( L"http=" );
lstrcpyW( szProxy, p );
}
p = wcschr( szProxy, ';' );
if (p) *p = 0;
FreeProxyInfo( lpwpi );
lpwpi->proxy = szProxy;
lpwpi->proxyBypass = NULL;
TRACE("http proxy (from registry) = %s\n", debugstr_w(lpwpi->proxy));
}
else
{
TRACE("No proxy server settings in registry.\n");
FreeProxyInfo( lpwpi );
lpwpi->proxy = NULL;
lpwpi->proxyBypass = NULL;
}
}
else if (envproxy)
{
FreeProxyInfo( lpwpi );
if (parse_proxy_url( lpwpi, envproxy ))
{
TRACE("http proxy (from environment) = %s\n", debugstr_w(lpwpi->proxy));
lpwpi->proxyEnabled = 1;
lpwpi->proxyBypass = NULL;
}
else
{
WARN("failed to parse http_proxy value %s\n", debugstr_w(envproxy));
lpwpi->proxyEnabled = 0;
lpwpi->proxy = NULL;
lpwpi->proxyBypass = NULL;
}
}
if (lpwpi->proxyEnabled)
{
TRACE("Proxy is enabled.\n");
if (!(envproxy = _wgetenv( L"no_proxy" )))
{
/* figure out how much memory the proxy setting takes */
if (!RegQueryValueExW( key, L"ProxyOverride", NULL, &type, NULL, &len ) && len && (type == REG_SZ))
{
LPWSTR szProxy;
if (!(szProxy = malloc( len )))
{
RegCloseKey( key );
return ERROR_OUTOFMEMORY;
}
RegQueryValueExW( key, L"ProxyOverride", NULL, &type, (BYTE*)szProxy, &len );
free( lpwpi->proxyBypass );
lpwpi->proxyBypass = szProxy;
TRACE("http proxy bypass (from registry) = %s\n", debugstr_w(lpwpi->proxyBypass));
}
else
{
free( lpwpi->proxyBypass );
lpwpi->proxyBypass = NULL;
TRACE("No proxy bypass server settings in registry.\n");
}
}
else
{
WCHAR *envproxyW;
if (!(envproxyW = malloc( wcslen(envproxy) * sizeof(WCHAR) )))
{
RegCloseKey( key );
return ERROR_OUTOFMEMORY;
}
lstrcpyW( envproxyW, envproxy );
free( lpwpi->proxyBypass );
lpwpi->proxyBypass = envproxyW;
TRACE("http proxy bypass (from environment) = %s\n", debugstr_w(lpwpi->proxyBypass));
}
}
else TRACE("Proxy is disabled.\n");
RegCloseKey( key );
return ERROR_SUCCESS;
}
/***********************************************************************
* INTERNET_ConfigureProxy
*/
static BOOL INTERNET_ConfigureProxy( appinfo_t *lpwai )
{
proxyinfo_t wpi;
if (INTERNET_LoadProxySettings( &wpi ))
return FALSE;
if (wpi.proxyEnabled)
{
TRACE("http proxy = %s bypass = %s\n", debugstr_w(wpi.proxy), debugstr_w(wpi.proxyBypass));
lpwai->accessType = INTERNET_OPEN_TYPE_PROXY;
lpwai->proxy = wpi.proxy;
lpwai->proxyBypass = wpi.proxyBypass;
lpwai->proxyUsername = wpi.proxyUsername;
lpwai->proxyPassword = wpi.proxyPassword;
return TRUE;
}
lpwai->accessType = INTERNET_OPEN_TYPE_DIRECT;
FreeProxyInfo(&wpi);
return FALSE;
}
/***********************************************************************
* dump_INTERNET_FLAGS
*
* Helper function to TRACE the internet flags.
*
* RETURNS
* None
*
*/
static void dump_INTERNET_FLAGS(DWORD dwFlags)
{
#define FE(x) { x, #x }
static const wininet_flag_info flag[] = {
FE(INTERNET_FLAG_RELOAD),
FE(INTERNET_FLAG_RAW_DATA),
FE(INTERNET_FLAG_EXISTING_CONNECT),
FE(INTERNET_FLAG_ASYNC),
FE(INTERNET_FLAG_PASSIVE),
FE(INTERNET_FLAG_NO_CACHE_WRITE),
FE(INTERNET_FLAG_MAKE_PERSISTENT),
FE(INTERNET_FLAG_FROM_CACHE),
FE(INTERNET_FLAG_SECURE),
FE(INTERNET_FLAG_KEEP_CONNECTION),
FE(INTERNET_FLAG_NO_AUTO_REDIRECT),
FE(INTERNET_FLAG_READ_PREFETCH),
FE(INTERNET_FLAG_NO_COOKIES),
FE(INTERNET_FLAG_NO_AUTH),
FE(INTERNET_FLAG_CACHE_IF_NET_FAIL),
FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP),
FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS),
FE(INTERNET_FLAG_IGNORE_CERT_DATE_INVALID),
FE(INTERNET_FLAG_IGNORE_CERT_CN_INVALID),
FE(INTERNET_FLAG_RESYNCHRONIZE),
FE(INTERNET_FLAG_HYPERLINK),
FE(INTERNET_FLAG_NO_UI),
FE(INTERNET_FLAG_PRAGMA_NOCACHE),
FE(INTERNET_FLAG_CACHE_ASYNC),
FE(INTERNET_FLAG_FORMS_SUBMIT),
FE(INTERNET_FLAG_NEED_FILE),
FE(INTERNET_FLAG_TRANSFER_ASCII),
FE(INTERNET_FLAG_TRANSFER_BINARY)
};
#undef FE
unsigned int i;
for (i = 0; i < ARRAY_SIZE(flag); i++) {
if (flag[i].val & dwFlags) {
TRACE(" %s", flag[i].name);
dwFlags &= ~flag[i].val;
}
}
if (dwFlags)
TRACE(" Unknown flags (%08lx)\n", dwFlags);
else
TRACE("\n");
}
/***********************************************************************
* INTERNET_CloseHandle (internal)
*
* Close internet handle
*
*/
static VOID APPINFO_Destroy(object_header_t *hdr)
{
appinfo_t *lpwai = (appinfo_t*)hdr;
TRACE("%p\n",lpwai);
free(lpwai->agent);
free(lpwai->proxy);
free(lpwai->proxyBypass);
free(lpwai->proxyUsername);
free(lpwai->proxyPassword);
}
static DWORD APPINFO_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
{
appinfo_t *ai = (appinfo_t*)hdr;
switch(option) {
case INTERNET_OPTION_HANDLE_TYPE:
TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
*size = sizeof(DWORD);
*(DWORD*)buffer = INTERNET_HANDLE_TYPE_INTERNET;
return ERROR_SUCCESS;
case INTERNET_OPTION_USER_AGENT: {
DWORD bufsize;
TRACE("INTERNET_OPTION_USER_AGENT\n");
bufsize = *size;
if (unicode) {
DWORD len = ai->agent ? lstrlenW(ai->agent) : 0;
*size = (len + 1) * sizeof(WCHAR);
if(!buffer || bufsize < *size)
return ERROR_INSUFFICIENT_BUFFER;
if (ai->agent)
lstrcpyW(buffer, ai->agent);
else
*(WCHAR *)buffer = 0;
/* If the buffer is copied, the returned length doesn't include
* the NULL terminator.
*/
*size = len;
}else {
if (ai->agent)
*size = WideCharToMultiByte(CP_ACP, 0, ai->agent, -1, NULL, 0, NULL, NULL);
else
*size = 1;
if(!buffer || bufsize < *size)
return ERROR_INSUFFICIENT_BUFFER;
if (ai->agent)
WideCharToMultiByte(CP_ACP, 0, ai->agent, -1, buffer, *size, NULL, NULL);
else
*(char *)buffer = 0;
/* If the buffer is copied, the returned length doesn't include
* the NULL terminator.
*/
*size -= 1;
}
return ERROR_SUCCESS;
}
case INTERNET_OPTION_PROXY:
if(!size) return ERROR_INVALID_PARAMETER;
if (unicode) {
INTERNET_PROXY_INFOW *pi = (INTERNET_PROXY_INFOW *)buffer;
DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
LPWSTR proxy, proxy_bypass;
if (ai->proxy)
proxyBytesRequired = (lstrlenW(ai->proxy) + 1) * sizeof(WCHAR);
if (ai->proxyBypass)
proxyBypassBytesRequired = (lstrlenW(ai->proxyBypass) + 1) * sizeof(WCHAR);
if (!pi || *size < sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired)
{
*size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
return ERROR_INSUFFICIENT_BUFFER;
}
proxy = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW));
proxy_bypass = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired);
pi->dwAccessType = ai->accessType;
pi->lpszProxy = NULL;
pi->lpszProxyBypass = NULL;
if (ai->proxy) {
lstrcpyW(proxy, ai->proxy);
pi->lpszProxy = proxy;
}
if (ai->proxyBypass) {
lstrcpyW(proxy_bypass, ai->proxyBypass);
pi->lpszProxyBypass = proxy_bypass;
}
*size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
return ERROR_SUCCESS;
}else {
INTERNET_PROXY_INFOA *pi = (INTERNET_PROXY_INFOA *)buffer;
DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
LPSTR proxy, proxy_bypass;
if (ai->proxy)
proxyBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->proxy, -1, NULL, 0, NULL, NULL);
if (ai->proxyBypass)
proxyBypassBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->proxyBypass, -1,
NULL, 0, NULL, NULL);
if (!pi || *size < sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired)
{
*size = sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired;
return ERROR_INSUFFICIENT_BUFFER;
}
proxy = (LPSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOA));
proxy_bypass = (LPSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired);
pi->dwAccessType = ai->accessType;
pi->lpszProxy = NULL;
pi->lpszProxyBypass = NULL;
if (ai->proxy) {
WideCharToMultiByte(CP_ACP, 0, ai->proxy, -1, proxy, proxyBytesRequired, NULL, NULL);
pi->lpszProxy = proxy;
}
if (ai->proxyBypass) {
WideCharToMultiByte(CP_ACP, 0, ai->proxyBypass, -1, proxy_bypass,
proxyBypassBytesRequired, NULL, NULL);
pi->lpszProxyBypass = proxy_bypass;
}
*size = sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired;
return ERROR_SUCCESS;
}
case INTERNET_OPTION_CONNECT_TIMEOUT:
TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
*(ULONG*)buffer = ai->connect_timeout;
*size = sizeof(ULONG);
return ERROR_SUCCESS;
}
return INET_QueryOption(hdr, option, buffer, size, unicode);
}
static DWORD APPINFO_SetOption(object_header_t *hdr, DWORD option, void *buf, DWORD size)
{
appinfo_t *ai = (appinfo_t*)hdr;
switch(option) {
case INTERNET_OPTION_CONNECT_TIMEOUT:
TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
if(size != sizeof(connect_timeout))
return ERROR_INTERNET_BAD_OPTION_LENGTH;
if(!*(ULONG*)buf)
return ERROR_BAD_ARGUMENTS;
ai->connect_timeout = *(ULONG*)buf;
return ERROR_SUCCESS;
case INTERNET_OPTION_USER_AGENT:
free(ai->agent);
if (!(ai->agent = wcsdup(buf))) return ERROR_OUTOFMEMORY;
return ERROR_SUCCESS;
case INTERNET_OPTION_REFRESH:
FIXME("INTERNET_OPTION_REFRESH\n");
return ERROR_SUCCESS;
}
return INET_SetOption(hdr, option, buf, size);
}
static const object_vtbl_t APPINFOVtbl = {
APPINFO_Destroy,
NULL,
APPINFO_QueryOption,
APPINFO_SetOption,
NULL,
NULL,
NULL,
NULL
};
/***********************************************************************
* InternetOpenW (WININET.@)
*
* Per-application initialization of wininet
*
* RETURNS
* HINTERNET on success
* NULL on failure
*
*/
HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
{
appinfo_t *lpwai = NULL;
if (TRACE_ON(wininet)) {
#define FE(x) { x, #x }
static const wininet_flag_info access_type[] = {
FE(INTERNET_OPEN_TYPE_PRECONFIG),
FE(INTERNET_OPEN_TYPE_DIRECT),
FE(INTERNET_OPEN_TYPE_PROXY),
FE(INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY)
};
#undef FE
DWORD i;
const char *access_type_str = "Unknown";
TRACE("(%s, %li, %s, %s, %li)\n", debugstr_w(lpszAgent), dwAccessType,
debugstr_w(lpszProxy), debugstr_w(lpszProxyBypass), dwFlags);
for (i = 0; i < ARRAY_SIZE(access_type); i++) {
if (access_type[i].val == dwAccessType) {
access_type_str = access_type[i].name;
break;
}
}
TRACE(" access type : %s\n", access_type_str);
TRACE(" flags :");
dump_INTERNET_FLAGS(dwFlags);
}
/* Clear any error information */
INTERNET_SetLastError(0);
if((dwAccessType == INTERNET_OPEN_TYPE_PROXY) && !lpszProxy) {
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
lpwai = alloc_object(NULL, &APPINFOVtbl, sizeof(appinfo_t));
if (!lpwai) {
SetLastError(ERROR_OUTOFMEMORY);
return NULL;
}
lpwai->hdr.htype = WH_HINIT;
lpwai->hdr.dwFlags = dwFlags;
lpwai->accessType = dwAccessType;
lpwai->proxyUsername = NULL;
lpwai->proxyPassword = NULL;
lpwai->connect_timeout = connect_timeout;
lpwai->agent = wcsdup(lpszAgent);
if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
INTERNET_ConfigureProxy( lpwai );
else if(dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
lpwai->proxy = wcsdup(lpszProxy);
lpwai->proxyBypass = wcsdup(lpszProxyBypass);
}
TRACE("returning %p\n", lpwai);
return lpwai->hdr.hInternet;
}
/***********************************************************************
* InternetOpenA (WININET.@)
*
* Per-application initialization of wininet
*
* RETURNS
* HINTERNET on success
* NULL on failure
*
*/
HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType,
LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
{
WCHAR *szAgent, *szProxy, *szBypass;
HINTERNET rc;
TRACE("(%s, 0x%08lx, %s, %s, 0x%08lx)\n", debugstr_a(lpszAgent),
dwAccessType, debugstr_a(lpszProxy), debugstr_a(lpszProxyBypass), dwFlags);
szAgent = strdupAtoW(lpszAgent);
szProxy = strdupAtoW(lpszProxy);
szBypass = strdupAtoW(lpszProxyBypass);
rc = InternetOpenW(szAgent, dwAccessType, szProxy, szBypass, dwFlags);
free(szAgent);
free(szProxy);
free(szBypass);
return rc;
}
/***********************************************************************
* InternetGetLastResponseInfoA (WININET.@)
*
* Return last wininet error description on the calling thread
*
* RETURNS
* TRUE on success of writing to buffer
* FALSE on failure
*
*/
BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
{
LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
TRACE("(%p, %p, %p)\n", lpdwError, lpszBuffer, lpdwBufferLength);
if (!lpdwError || !lpdwBufferLength)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpwite)
{
if (lpszBuffer == NULL || *lpdwBufferLength < strlen(lpwite->response))
{
*lpdwBufferLength = strlen(lpwite->response);
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*lpdwError = lpwite->dwError;
if (*lpdwBufferLength)
{
memcpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
lpszBuffer[*lpdwBufferLength - 1] = 0;
*lpdwBufferLength = strlen(lpszBuffer);
}
}
else
{
*lpdwError = 0;
*lpdwBufferLength = 0;
}
return TRUE;
}
/***********************************************************************
* InternetGetLastResponseInfoW (WININET.@)
*
* Return last wininet error description on the calling thread
*
* RETURNS
* TRUE on success of writing to buffer
* FALSE on failure
*
*/
BOOL WINAPI InternetGetLastResponseInfoW(LPDWORD lpdwError,
LPWSTR lpszBuffer, LPDWORD lpdwBufferLength)
{
LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
TRACE("(%p, %p, %p)\n", lpdwError, lpszBuffer, lpdwBufferLength);
if (!lpdwError || !lpdwBufferLength)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (lpwite)
{
int required_size = MultiByteToWideChar(CP_ACP, 0, lpwite->response, -1, NULL, 0) - 1;
if (lpszBuffer == NULL || *lpdwBufferLength < required_size)
{
*lpdwBufferLength = required_size;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*lpdwError = lpwite->dwError;
if (*lpdwBufferLength)
*lpdwBufferLength = MultiByteToWideChar(CP_ACP, 0, lpwite->response, -1, lpszBuffer, *lpdwBufferLength);
}
else
{
*lpdwError = 0;
*lpdwBufferLength = 0;
}
return TRUE;
}
/***********************************************************************
* InternetGetConnectedState (WININET.@)
*
* Return connected state
*
* RETURNS
* TRUE if connected
* if lpdwStatus is not null, return the status (off line,
* modem, lan...) in it.
* FALSE if not connected
*/
BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
{
TRACE("(%p, 0x%08lx)\n", lpdwStatus, dwReserved);
return InternetGetConnectedStateExW(lpdwStatus, NULL, 0, dwReserved);
}
/***********************************************************************
* InternetGetConnectedStateExW (WININET.@)
*
* Return connected state
*
* PARAMS
*
* lpdwStatus [O] Flags specifying the status of the internet connection.
* lpszConnectionName [O] Pointer to buffer to receive the friendly name of the internet connection.
* dwNameLen [I] Size of the buffer, in characters.
* dwReserved [I] Reserved. Must be set to 0.
*
* RETURNS
* TRUE if connected
* if lpdwStatus is not null, return the status (off line,
* modem, lan...) in it.
* FALSE if not connected
*
* NOTES
* If the system has no available network connections, an empty string is
* stored in lpszConnectionName. If there is a LAN connection, a localized
* "LAN Connection" string is stored. Presumably, if only a dial-up
* connection is available then the name of the dial-up connection is
* returned. Why any application, other than the "Internet Settings" CPL,
* would want to use this function instead of the simpler InternetGetConnectedStateW
* function is beyond me.
*/
BOOL WINAPI InternetGetConnectedStateExW(LPDWORD lpdwStatus, LPWSTR lpszConnectionName,
DWORD dwNameLen, DWORD dwReserved)
{
IP_ADAPTER_ADDRESSES *buf = NULL, *aa;
ULONG size = 0;
DWORD status;
TRACE("(%p, %p, %ld, 0x%08lx)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
/* Must be zero */
if(dwReserved)
return FALSE;
for (;;)
{
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_INCLUDE_ALL_GATEWAYS;
ULONG errcode = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, buf, &size);
if (errcode == ERROR_SUCCESS)
break;
free(buf);
if (errcode == ERROR_BUFFER_OVERFLOW && !(buf = malloc(size)))
errcode = ERROR_NOT_ENOUGH_MEMORY;
if (errcode != ERROR_BUFFER_OVERFLOW)
{
if (errcode != ERROR_NO_DATA)
{
SetLastError(errcode);
return FALSE;
}
buf = NULL;
break;
}
}
status = INTERNET_RAS_INSTALLED;
for (aa = buf; aa; aa = aa->Next)
{
/* Connected, but not necessarily to internet */
if (aa->FirstUnicastAddress)
status |= INTERNET_CONNECTION_OFFLINE;
/* Connected to internet */
if (aa->FirstGatewayAddress)
{
WARN("always returning LAN connection.\n");
status &= ~INTERNET_CONNECTION_OFFLINE;
status |= INTERNET_CONNECTION_LAN;
break;
}
}
free(buf);
if (lpdwStatus) *lpdwStatus = status;
/* When the buffer size is zero LoadStringW fills the buffer with a pointer to
* the resource, avoid it as we must not change the buffer in this case */
if (lpszConnectionName && dwNameLen)
{
*lpszConnectionName = '\0';
if (status & INTERNET_CONNECTION_LAN)
LoadStringW(WININET_hModule, IDS_LANCONNECTION, lpszConnectionName, dwNameLen);
}
if (!(status & (INTERNET_CONNECTION_LAN | INTERNET_CONNECTION_MODEM | INTERNET_CONNECTION_PROXY)))
{
SetLastError(ERROR_SUCCESS);
return FALSE;
}
return TRUE;
}
/***********************************************************************
* InternetGetConnectedStateExA (WININET.@)
*/
BOOL WINAPI InternetGetConnectedStateExA(LPDWORD lpdwStatus, LPSTR lpszConnectionName,
DWORD dwNameLen, DWORD dwReserved)
{
LPWSTR lpwszConnectionName = NULL;
BOOL rc;
TRACE("(%p, %p, %ld, 0x%08lx)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
if (lpszConnectionName && dwNameLen > 0)
lpwszConnectionName = malloc(dwNameLen * sizeof(WCHAR));
rc = InternetGetConnectedStateExW(lpdwStatus,lpwszConnectionName, dwNameLen,
dwReserved);
if (rc && lpwszConnectionName)
WideCharToMultiByte(CP_ACP,0,lpwszConnectionName,-1,lpszConnectionName,
dwNameLen, NULL, NULL);
free(lpwszConnectionName);
return rc;
}
/***********************************************************************
* InternetConnectW (WININET.@)
*
* Open a ftp, gopher or http session
*
* RETURNS
* HINTERNET a session handle on success
* NULL on failure
*
*/
HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
LPCWSTR lpszUserName, LPCWSTR lpszPassword,
DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
{
appinfo_t *hIC;
HINTERNET rc = NULL;
DWORD res = ERROR_SUCCESS;
TRACE("(%p, %s, %u, %s, %p, %lu, %lx, %Ix)\n", hInternet, debugstr_w(lpszServerName),
nServerPort, debugstr_w(lpszUserName), lpszPassword, dwService, dwFlags, dwContext);
if (!lpszServerName)
{
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
hIC = (appinfo_t*)get_handle_object( hInternet );
if ( (hIC == NULL) || (hIC->hdr.htype != WH_HINIT) )
{
res = ERROR_INVALID_HANDLE;
goto lend;
}
switch (dwService)
{
case INTERNET_SERVICE_FTP:
rc = FTP_Connect(hIC, lpszServerName, nServerPort,
lpszUserName, lpszPassword, dwFlags, dwContext, 0);
if(!rc)
res = INTERNET_GetLastError();
break;
case INTERNET_SERVICE_HTTP:
res = HTTP_Connect(hIC, lpszServerName, nServerPort,
lpszUserName, lpszPassword, dwFlags, dwContext, 0, &rc);
break;
case INTERNET_SERVICE_GOPHER:
default:
break;
}
lend:
if( hIC )
WININET_Release( &hIC->hdr );
TRACE("returning %p\n", rc);
SetLastError(res);
return rc;
}
/***********************************************************************
* InternetConnectA (WININET.@)
*
* Open a ftp, gopher or http session
*
* RETURNS
* HINTERNET a session handle on success
* NULL on failure
*
*/
HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
LPCSTR lpszServerName, INTERNET_PORT nServerPort,
LPCSTR lpszUserName, LPCSTR lpszPassword,
DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
{
HINTERNET rc = NULL;
LPWSTR szServerName;
LPWSTR szUserName;
LPWSTR szPassword;
szServerName = strdupAtoW(lpszServerName);
szUserName = strdupAtoW(lpszUserName);
szPassword = strdupAtoW(lpszPassword);
rc = InternetConnectW(hInternet, szServerName, nServerPort,
szUserName, szPassword, dwService, dwFlags, dwContext);
free(szServerName);
free(szUserName);
free(szPassword);
return rc;
}
/***********************************************************************
* InternetFindNextFileA (WININET.@)
*
* Continues a file search from a previous call to FindFirstFile
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
{
BOOL ret;
WIN32_FIND_DATAW fd;
ret = InternetFindNextFileW(hFind, lpvFindData?&fd:NULL);
if(lpvFindData)
WININET_find_data_WtoA(&fd, (LPWIN32_FIND_DATAA)lpvFindData);
return ret;
}
/***********************************************************************
* InternetFindNextFileW (WININET.@)
*
* Continues a file search from a previous call to FindFirstFile
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetFindNextFileW(HINTERNET hFind, LPVOID lpvFindData)
{
object_header_t *hdr;
DWORD res;
TRACE("\n");
hdr = get_handle_object(hFind);
if(!hdr) {
WARN("Invalid handle\n");
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(hdr->vtbl->FindNextFileW) {
res = hdr->vtbl->FindNextFileW(hdr, lpvFindData);
}else {
WARN("Handle doesn't support NextFile\n");
res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
}
WININET_Release(hdr);
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
/***********************************************************************
* InternetCloseHandle (WININET.@)
*
* Generic close handle function
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
{
object_header_t *obj;
TRACE("%p\n", hInternet);
obj = get_handle_object( hInternet );
if (!obj) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
invalidate_handle(obj);
WININET_Release(obj);
return TRUE;
}
static BOOL set_url_component(WCHAR **component, DWORD *component_length, const WCHAR *value, DWORD len)
{
TRACE("%s (%ld)\n", debugstr_wn(value, len), len);
if (!*component_length)
return TRUE;
if (!*component) {
*(const WCHAR**)component = value;
*component_length = len;
return TRUE;
}
if (*component_length < len+1) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*component_length = len;
if(len)
memcpy(*component, value, len*sizeof(WCHAR));
(*component)[len] = 0;
return TRUE;
}
static BOOL set_url_component_WtoA(const WCHAR *comp_w, DWORD length, const WCHAR *url_w, char **comp, DWORD *ret_length,
const char *url_a)
{
size_t size, ret_size = *ret_length;
if (!*ret_length)
return TRUE;
size = WideCharToMultiByte(CP_ACP, 0, comp_w, length, NULL, 0, NULL, NULL);
if (!*comp) {
*comp = comp_w ? (char*)url_a + WideCharToMultiByte(CP_ACP, 0, url_w, comp_w-url_w, NULL, 0, NULL, NULL) : NULL;
*ret_length = size;
return TRUE;
}
if (size+1 > ret_size) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
*ret_length = size+1;
return FALSE;
}
*ret_length = size;
WideCharToMultiByte(CP_ACP, 0, comp_w, length, *comp, ret_size-1, NULL, NULL);
(*comp)[size] = 0;
return TRUE;
}
static BOOL set_url_component_AtoW(const char *comp_a, DWORD len_a, WCHAR **comp_w, DWORD *len_w, WCHAR **buf)
{
*len_w = len_a;
if(!comp_a) {
*comp_w = NULL;
return TRUE;
}
if(!(*comp_w = *buf = malloc(len_a * sizeof(WCHAR)))) {
SetLastError(ERROR_OUTOFMEMORY);
return FALSE;
}
return TRUE;
}
/***********************************************************************
* InternetCrackUrlA (WININET.@)
*
* See InternetCrackUrlW.
*/
BOOL WINAPI InternetCrackUrlA(const char *url, DWORD url_length, DWORD flags, URL_COMPONENTSA *ret_comp)
{
WCHAR *host = NULL, *user = NULL, *pass = NULL, *path = NULL, *scheme = NULL, *extra = NULL;
URL_COMPONENTSW comp;
WCHAR *url_w = NULL;
BOOL ret;
TRACE("(%s %lu %lx %p)\n", url_length ? debugstr_an(url, url_length) : debugstr_a(url), url_length, flags, ret_comp);
if (!url || !*url || !ret_comp || ret_comp->dwStructSize != sizeof(URL_COMPONENTSA)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
comp.dwStructSize = sizeof(comp);
ret = set_url_component_AtoW(ret_comp->lpszHostName, ret_comp->dwHostNameLength,
&comp.lpszHostName, &comp.dwHostNameLength, &host)
&& set_url_component_AtoW(ret_comp->lpszUserName, ret_comp->dwUserNameLength,
&comp.lpszUserName, &comp.dwUserNameLength, &user)
&& set_url_component_AtoW(ret_comp->lpszPassword, ret_comp->dwPasswordLength,
&comp.lpszPassword, &comp.dwPasswordLength, &pass)
&& set_url_component_AtoW(ret_comp->lpszUrlPath, ret_comp->dwUrlPathLength,
&comp.lpszUrlPath, &comp.dwUrlPathLength, &path)
&& set_url_component_AtoW(ret_comp->lpszScheme, ret_comp->dwSchemeLength,
&comp.lpszScheme, &comp.dwSchemeLength, &scheme)
&& set_url_component_AtoW(ret_comp->lpszExtraInfo, ret_comp->dwExtraInfoLength,
&comp.lpszExtraInfo, &comp.dwExtraInfoLength, &extra);
if(ret && !(url_w = strndupAtoW(url, url_length ? url_length : -1, &url_length))) {
SetLastError(ERROR_OUTOFMEMORY);
ret = FALSE;
}
if (ret && (ret = InternetCrackUrlW(url_w, url_length, flags, &comp))) {
ret_comp->nScheme = comp.nScheme;
ret_comp->nPort = comp.nPort;
ret = set_url_component_WtoA(comp.lpszHostName, comp.dwHostNameLength, url_w,
&ret_comp->lpszHostName, &ret_comp->dwHostNameLength, url)
&& set_url_component_WtoA(comp.lpszUserName, comp.dwUserNameLength, url_w,
&ret_comp->lpszUserName, &ret_comp->dwUserNameLength, url)
&& set_url_component_WtoA(comp.lpszPassword, comp.dwPasswordLength, url_w,
&ret_comp->lpszPassword, &ret_comp->dwPasswordLength, url)
&& set_url_component_WtoA(comp.lpszUrlPath, comp.dwUrlPathLength, url_w,
&ret_comp->lpszUrlPath, &ret_comp->dwUrlPathLength, url)
&& set_url_component_WtoA(comp.lpszScheme, comp.dwSchemeLength, url_w,
&ret_comp->lpszScheme, &ret_comp->dwSchemeLength, url)
&& set_url_component_WtoA(comp.lpszExtraInfo, comp.dwExtraInfoLength, url_w,
&ret_comp->lpszExtraInfo, &ret_comp->dwExtraInfoLength, url);
if(ret)
TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_a(url),
debugstr_an(ret_comp->lpszScheme, ret_comp->dwSchemeLength),
debugstr_an(ret_comp->lpszHostName, ret_comp->dwHostNameLength),
debugstr_an(ret_comp->lpszUrlPath, ret_comp->dwUrlPathLength),
debugstr_an(ret_comp->lpszExtraInfo, ret_comp->dwExtraInfoLength));
}
free(host);
free(user);
free(pass);
free(path);
free(scheme);
free(extra);
free(url_w);
return ret;
}
static const WCHAR *url_schemes[] =
{
L"ftp",
L"gopher",
L"http",
L"https",
L"file",
L"news",
L"mailto",
L"socks",
L"javascript",
L"vbscript",
L"res"
};
/***********************************************************************
* GetInternetSchemeW (internal)
*
* Get scheme of url
*
* RETURNS
* scheme on success
* INTERNET_SCHEME_UNKNOWN on failure
*
*/
static INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, DWORD nMaxCmp)
{
int i;
TRACE("%s %ld\n",debugstr_wn(lpszScheme, nMaxCmp), nMaxCmp);
if(lpszScheme==NULL)
return INTERNET_SCHEME_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(url_schemes); i++)
if (!wcsnicmp(lpszScheme, url_schemes[i], nMaxCmp))
return INTERNET_SCHEME_FIRST + i;
return INTERNET_SCHEME_UNKNOWN;
}
/***********************************************************************
* InternetCrackUrlW (WININET.@)
*
* Break up URL into its components
*
* RETURNS
* TRUE on success
* FALSE on failure
*/
BOOL WINAPI InternetCrackUrlW(const WCHAR *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSW *lpUC)
{
/*
* RFC 1808
* <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
*
*/
LPCWSTR lpszParam = NULL;
BOOL found_colon = FALSE;
LPCWSTR lpszap;
LPCWSTR lpszcp = NULL, lpszNetLoc;
TRACE("(%s %lu %lx %p)\n",
lpszUrl ? debugstr_wn(lpszUrl, dwUrlLength ? dwUrlLength : lstrlenW(lpszUrl)) : "(null)",
dwUrlLength, dwFlags, lpUC);
if (!lpszUrl || !*lpszUrl || !lpUC)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!dwUrlLength)
dwUrlLength = lstrlenW(lpszUrl);
else {
/* Windows stops at a null, regardless of what dwUrlLength says. */
dwUrlLength = wcsnlen(lpszUrl, dwUrlLength);
}
if (dwFlags & ICU_DECODE)
{
WCHAR *url_tmp, *buffer;
DWORD len = dwUrlLength + 1;
BOOL ret;
if (!(url_tmp = strndupW(lpszUrl, dwUrlLength)))
{
SetLastError(ERROR_OUTOFMEMORY);
return FALSE;
}
buffer = url_tmp;
ret = InternetCanonicalizeUrlW(url_tmp, buffer, &len, ICU_DECODE | ICU_NO_ENCODE);
if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
buffer = malloc(len * sizeof(WCHAR));
if (!buffer)
{
SetLastError(ERROR_OUTOFMEMORY);
free(url_tmp);
return FALSE;
}
ret = InternetCanonicalizeUrlW(url_tmp, buffer, &len, ICU_DECODE | ICU_NO_ENCODE);
}
if (ret)
ret = InternetCrackUrlW(buffer, len, dwFlags & ~ICU_DECODE, lpUC);
if (buffer != url_tmp) free(buffer);
free(url_tmp);
return ret;
}
lpszap = lpszUrl;
/* Determine if the URI is absolute. */
while (lpszap - lpszUrl < dwUrlLength)
{
if (iswalnum(*lpszap) || *lpszap == '+' || *lpszap == '.' || *lpszap == '-')
{
lpszap++;
continue;
}
if (*lpszap == ':')
{
found_colon = TRUE;
lpszcp = lpszap;
}
else
{
lpszcp = lpszUrl; /* Relative url */
}
break;
}
if(!found_colon){
SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
return FALSE;
}
lpUC->nScheme = INTERNET_SCHEME_UNKNOWN;
lpUC->nPort = INTERNET_INVALID_PORT_NUMBER;
/* Parse <params> */
lpszParam = wmemchr(lpszap, '?', dwUrlLength - (lpszap - lpszUrl));
if(!lpszParam)
lpszParam = wmemchr(lpszap, '#', dwUrlLength - (lpszap - lpszUrl));
if(!set_url_component(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
lpszParam, lpszParam ? dwUrlLength-(lpszParam-lpszUrl) : 0))
return FALSE;
/* Get scheme first. */
lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
if(!set_url_component(&lpUC->lpszScheme, &lpUC->dwSchemeLength, lpszUrl, lpszcp - lpszUrl))
return FALSE;
/* Eat ':' in protocol. */
lpszcp++;
/* double slash indicates the net_loc portion is present */
if ((lpszcp[0] == '/') && (lpszcp[1] == '/'))
{
lpszcp += 2;
lpszNetLoc = wmemchr(lpszcp, '/', dwUrlLength - (lpszcp - lpszUrl));
if (lpszParam)
{
if (lpszNetLoc)
lpszNetLoc = min(lpszNetLoc, lpszParam);
else
lpszNetLoc = lpszParam;
}
else if (!lpszNetLoc)
lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
/* Parse net-loc */
if (lpszNetLoc)
{
LPCWSTR lpszHost;
LPCWSTR lpszPort;
/* [<user>[<:password>]@]<host>[:<port>] */
/* First find the user and password if they exist */
lpszHost = wmemchr(lpszcp, '@', dwUrlLength - (lpszcp - lpszUrl));
if (lpszHost == NULL || lpszHost > lpszNetLoc)
{
/* username and password not specified. */
set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
}
else /* Parse out username and password */
{
LPCWSTR lpszUser = lpszcp;
LPCWSTR lpszPasswd = lpszHost;
while (lpszcp < lpszHost)
{
if (*lpszcp == ':')
lpszPasswd = lpszcp;
lpszcp++;
}
if(!set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, lpszUser, lpszPasswd - lpszUser))
return FALSE;
if (lpszPasswd != lpszHost)
lpszPasswd++;
if(!set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
lpszPasswd == lpszHost ? NULL : lpszPasswd, lpszHost - lpszPasswd))
return FALSE;
lpszcp++; /* Advance to beginning of host */
}
/* Parse <host><:port> */
lpszHost = lpszcp;
lpszPort = lpszNetLoc;
/* special case for res:// URLs: there is no port here, so the host is the
entire string up to the first '/' */
if(lpUC->nScheme==INTERNET_SCHEME_RES)
{
if(!set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, lpszHost, lpszPort - lpszHost))
return FALSE;
lpszcp=lpszNetLoc;
}
else
{
while (lpszcp < lpszNetLoc)
{
if (*lpszcp == ':')
lpszPort = lpszcp;
lpszcp++;
}
/* If the scheme is "file" and the host is just one letter, it's not a host */
if(lpUC->nScheme==INTERNET_SCHEME_FILE && lpszPort <= lpszHost+1)
{
lpszcp=lpszHost;
set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
}
else
{
if(!set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, lpszHost, lpszPort - lpszHost))
return FALSE;
if (lpszPort != lpszNetLoc)
lpUC->nPort = wcstol(++lpszPort, NULL, 10);
else switch (lpUC->nScheme)
{
case INTERNET_SCHEME_HTTP:
lpUC->nPort = INTERNET_DEFAULT_HTTP_PORT;
break;
case INTERNET_SCHEME_HTTPS:
lpUC->nPort = INTERNET_DEFAULT_HTTPS_PORT;
break;
case INTERNET_SCHEME_FTP:
lpUC->nPort = INTERNET_DEFAULT_FTP_PORT;
break;
default:
break;
}
}
}
}
}
else
{
set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
}
/* Here lpszcp points to:
*
* <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*/
if (lpszcp != 0 && lpszcp - lpszUrl < dwUrlLength && (!lpszParam || lpszcp <= lpszParam))
{
DWORD len;
/* Only truncate the parameter list if it's already been saved
* in lpUC->lpszExtraInfo.
*/
if (lpszParam && lpUC->dwExtraInfoLength && lpUC->lpszExtraInfo)
len = lpszParam - lpszcp;
else
{
/* Leave the parameter list in lpszUrlPath. Strip off any trailing
* newlines if necessary.
*/
LPWSTR lpsznewline = wmemchr(lpszcp, '\n', dwUrlLength - (lpszcp - lpszUrl));
if (lpsznewline != NULL)
len = lpsznewline - lpszcp;
else
len = dwUrlLength-(lpszcp-lpszUrl);
}
if (lpUC->dwUrlPathLength && lpUC->lpszUrlPath &&
lpUC->nScheme == INTERNET_SCHEME_FILE)
{
WCHAR tmppath[MAX_PATH];
if (*lpszcp == '/')
{
len = MAX_PATH;
PathCreateFromUrlW(lpszUrl, tmppath, &len, 0);
}
else
{
WCHAR *iter;
memcpy(tmppath, lpszcp, len * sizeof(WCHAR));
tmppath[len] = '\0';
iter = tmppath;
while (*iter) {
if (*iter == '/')
*iter = '\\';
++iter;
}
}
/* if ends in \. or \.. append a backslash */
if (tmppath[len - 1] == '.' &&
(tmppath[len - 2] == '\\' ||
(tmppath[len - 2] == '.' && tmppath[len - 3] == '\\')))
{
if (len < MAX_PATH - 1)
{
tmppath[len] = '\\';
tmppath[len+1] = '\0';
++len;
}
}
if(!set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, tmppath, len))
return FALSE;
}
else if(!set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, lpszcp, len))
return FALSE;
}
else
{
set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, lpszcp, 0);
}
TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
debugstr_wn(lpUC->lpszScheme,lpUC->dwSchemeLength),
debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
return TRUE;
}
/***********************************************************************
* InternetAttemptConnect (WININET.@)
*
* Attempt to make a connection to the internet
*
* RETURNS
* ERROR_SUCCESS on success
* Error value on failure
*
*/
DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
{
FIXME("Stub\n");
return ERROR_SUCCESS;
}
/***********************************************************************
* convert_url_canonicalization_flags
*
* Helper for InternetCanonicalizeUrl
*
* PARAMS
* dwFlags [I] Flags suitable for InternetCanonicalizeUrl
*
* RETURNS
* Flags suitable for UrlCanonicalize
*/
static DWORD convert_url_canonicalization_flags(DWORD dwFlags)
{
DWORD dwUrlFlags = URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE;
if (dwFlags & ICU_BROWSER_MODE) dwUrlFlags |= URL_BROWSER_MODE;
if (dwFlags & ICU_DECODE) dwUrlFlags |= URL_UNESCAPE;
if (dwFlags & ICU_ENCODE_PERCENT) dwUrlFlags |= URL_ESCAPE_PERCENT;
if (dwFlags & ICU_ENCODE_SPACES_ONLY) dwUrlFlags |= URL_ESCAPE_SPACES_ONLY;
/* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
if (dwFlags & ICU_NO_ENCODE) dwUrlFlags ^= URL_ESCAPE_UNSAFE;
if (dwFlags & ICU_NO_META) dwUrlFlags |= URL_NO_META;
return dwUrlFlags;
}
/***********************************************************************
* InternetCanonicalizeUrlA (WININET.@)
*
* Escape unsafe characters and spaces
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
LPDWORD lpdwBufferLength, DWORD dwFlags)
{
HRESULT hr;
TRACE("(%s, %p, %p, 0x%08lx) buffer length: %ld\n", debugstr_a(lpszUrl), lpszBuffer,
lpdwBufferLength, dwFlags, lpdwBufferLength ? *lpdwBufferLength : -1);
dwFlags = convert_url_canonicalization_flags(dwFlags);
hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
if (hr == E_POINTER) SetLastError(ERROR_INSUFFICIENT_BUFFER);
if (hr == E_INVALIDARG) SetLastError(ERROR_INVALID_PARAMETER);
return hr == S_OK;
}
/***********************************************************************
* InternetCanonicalizeUrlW (WININET.@)
*
* Escape unsafe characters and spaces
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
LPDWORD lpdwBufferLength, DWORD dwFlags)
{
HRESULT hr;
TRACE("(%s, %p, %p, 0x%08lx) buffer length: %ld\n", debugstr_w(lpszUrl), lpszBuffer,
lpdwBufferLength, dwFlags, lpdwBufferLength ? *lpdwBufferLength : -1);
dwFlags = convert_url_canonicalization_flags(dwFlags);
hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
if (hr == E_POINTER) SetLastError(ERROR_INSUFFICIENT_BUFFER);
if (hr == E_INVALIDARG) SetLastError(ERROR_INVALID_PARAMETER);
return hr == S_OK;
}
/* #################################################### */
static INTERNET_STATUS_CALLBACK set_status_callback(
object_header_t *lpwh, INTERNET_STATUS_CALLBACK callback, BOOL unicode)
{
INTERNET_STATUS_CALLBACK ret;
if (unicode) lpwh->dwInternalFlags |= INET_CALLBACKW;
else lpwh->dwInternalFlags &= ~INET_CALLBACKW;
ret = lpwh->lpfnStatusCB;
lpwh->lpfnStatusCB = callback;
return ret;
}
/***********************************************************************
* InternetSetStatusCallbackA (WININET.@)
*
* Sets up a callback function which is called as progress is made
* during an operation.
*
* RETURNS
* Previous callback or NULL on success
* INTERNET_INVALID_STATUS_CALLBACK on failure
*
*/
INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
{
INTERNET_STATUS_CALLBACK retVal;
object_header_t *lpwh;
TRACE("%p\n", hInternet);
if (!(lpwh = get_handle_object(hInternet)))
return INTERNET_INVALID_STATUS_CALLBACK;
retVal = set_status_callback(lpwh, lpfnIntCB, FALSE);
WININET_Release( lpwh );
return retVal;
}
/***********************************************************************
* InternetSetStatusCallbackW (WININET.@)
*
* Sets up a callback function which is called as progress is made
* during an operation.
*
* RETURNS
* Previous callback or NULL on success
* INTERNET_INVALID_STATUS_CALLBACK on failure
*
*/
INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(
HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
{
INTERNET_STATUS_CALLBACK retVal;
object_header_t *lpwh;
TRACE("%p\n", hInternet);
if (!(lpwh = get_handle_object(hInternet)))
return INTERNET_INVALID_STATUS_CALLBACK;
retVal = set_status_callback(lpwh, lpfnIntCB, TRUE);
WININET_Release( lpwh );
return retVal;
}
/***********************************************************************
* InternetSetFilePointer (WININET.@)
*
* Sets read position for an open internet file.
*
* RETURNS
* Current position of the file on success
* INVALID_SET_FILE_POINTER on failure
*/
DWORD WINAPI InternetSetFilePointer(HINTERNET hFile, LONG lDistanceToMove,
PVOID pReserved, DWORD dwMoveContext, DWORD_PTR dwContext)
{
object_header_t *hdr;
DWORD res;
TRACE("(%p %ld %p %ld %Ix)\n", hFile, lDistanceToMove, pReserved, dwMoveContext, dwContext);
hdr = get_handle_object(hFile);
if(!hdr) {
SetLastError(ERROR_INVALID_HANDLE);
return INVALID_SET_FILE_POINTER;
}
if(hdr->vtbl->SetFilePointer) {
res = hdr->vtbl->SetFilePointer(hdr, lDistanceToMove, dwMoveContext);
}else {
SetLastError(ERROR_INVALID_HANDLE);
res = INVALID_SET_FILE_POINTER;
}
WININET_Release(hdr);
return res;
}
/***********************************************************************
* InternetWriteFile (WININET.@)
*
* Write data to an open internet file
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer,
DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
{
object_header_t *lpwh;
BOOL res;
TRACE("(%p %p %ld %p)\n", hFile, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
lpwh = get_handle_object( hFile );
if (!lpwh) {
WARN("Invalid handle\n");
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(lpwh->vtbl->WriteFile) {
res = lpwh->vtbl->WriteFile(lpwh, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
}else {
WARN("No Writefile method.\n");
res = ERROR_INVALID_HANDLE;
}
WININET_Release( lpwh );
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
/***********************************************************************
* InternetReadFile (WININET.@)
*
* Read data from an open internet file
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead)
{
object_header_t *hdr;
DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
TRACE("%p %p %ld %p\n", hFile, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead);
hdr = get_handle_object(hFile);
if (!hdr) {
INTERNET_SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(hdr->vtbl->ReadFile) {
res = hdr->vtbl->ReadFile(hdr, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead, 0, 0);
if(res == ERROR_IO_PENDING)
*pdwNumOfBytesRead = 0;
}
WININET_Release(hdr);
TRACE("-- %s (%lu) (bytes read: %ld)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE", res,
pdwNumOfBytesRead ? *pdwNumOfBytesRead : -1);
SetLastError(res);
return res == ERROR_SUCCESS;
}
/***********************************************************************
* InternetReadFileExA (WININET.@)
*
* Read data from an open internet file
*
* PARAMS
* hFile [I] Handle returned by InternetOpenUrl or HttpOpenRequest.
* lpBuffersOut [I/O] Buffer.
* dwFlags [I] Flags. See notes.
* dwContext [I] Context for callbacks.
*
* RETURNS
* TRUE on success
* FALSE on failure
*
* NOTES
* The parameter dwFlags include zero or more of the following flags:
*|IRF_ASYNC - Makes the call asynchronous.
*|IRF_SYNC - Makes the call synchronous.
*|IRF_USE_CONTEXT - Forces dwContext to be used.
*|IRF_NO_WAIT - Don't block if the data is not available, just return what is available.
*
* However, in testing IRF_USE_CONTEXT seems to have no effect - dwContext isn't used.
*
* SEE
* InternetOpenUrlA(), HttpOpenRequestA()
*/
BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffersOut,
DWORD dwFlags, DWORD_PTR dwContext)
{
object_header_t *hdr;
DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
TRACE("(%p %p 0x%lx 0x%Ix)\n", hFile, lpBuffersOut, dwFlags, dwContext);
if (lpBuffersOut->dwStructSize != sizeof(*lpBuffersOut)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
hdr = get_handle_object(hFile);
if (!hdr) {
INTERNET_SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(hdr->vtbl->ReadFile)
res = hdr->vtbl->ReadFile(hdr, lpBuffersOut->lpvBuffer, lpBuffersOut->dwBufferLength,
&lpBuffersOut->dwBufferLength, dwFlags, dwContext);
WININET_Release(hdr);
TRACE("-- %s (%lu, bytes read: %ld)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE",
res, lpBuffersOut->dwBufferLength);
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
/***********************************************************************
* InternetReadFileExW (WININET.@)
* SEE
* InternetReadFileExA()
*/
BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
DWORD dwFlags, DWORD_PTR dwContext)
{
object_header_t *hdr;
DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
TRACE("(%p %p 0x%lx 0x%Ix)\n", hFile, lpBuffer, dwFlags, dwContext);
if (!lpBuffer || lpBuffer->dwStructSize != sizeof(*lpBuffer)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
hdr = get_handle_object(hFile);
if (!hdr) {
INTERNET_SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(hdr->vtbl->ReadFile)
res = hdr->vtbl->ReadFile(hdr, lpBuffer->lpvBuffer, lpBuffer->dwBufferLength, &lpBuffer->dwBufferLength,
dwFlags, dwContext);
WININET_Release(hdr);
TRACE("-- %s (%lu, bytes read: %ld)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE",
res, lpBuffer->dwBufferLength);
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
static IP_ADAPTER_ADDRESSES *get_adapters(void)
{
ULONG err, size = 1024, flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
IP_ADAPTER_ADDRESSES *tmp, *ret;
if (!(ret = malloc( size ))) return NULL;
err = GetAdaptersAddresses( AF_UNSPEC, flags, NULL, ret, &size );
while (err == ERROR_BUFFER_OVERFLOW)
{
if (!(tmp = realloc( ret, size ))) break;
ret = tmp;
err = GetAdaptersAddresses( AF_UNSPEC, flags, NULL, ret, &size );
}
if (err == ERROR_SUCCESS) return ret;
free( ret );
return NULL;
}
static WCHAR *detect_proxy_autoconfig_url_dhcp(void)
{
IP_ADAPTER_ADDRESSES *adapters, *ptr;
DHCPCAPI_PARAMS_ARRAY send_params, recv_params;
DHCPCAPI_PARAMS param;
WCHAR name[MAX_ADAPTER_NAME_LENGTH + 1], *ret = NULL;
DWORD err, size;
BYTE *tmp, *buf = NULL;
if (!(adapters = get_adapters())) return NULL;
memset( &send_params, 0, sizeof(send_params) );
memset( &param, 0, sizeof(param) );
param.OptionId = OPTION_MSFT_IE_PROXY;
recv_params.nParams = 1;
recv_params.Params = &param;
for (ptr = adapters; ptr; ptr = ptr->Next)
{
MultiByteToWideChar( CP_ACP, 0, ptr->AdapterName, -1, name, ARRAY_SIZE(name) );
TRACE( "adapter '%s' type %lu dhcpv4 enabled %d\n", wine_dbgstr_w(name), ptr->IfType, ptr->Dhcpv4Enabled );
if (ptr->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue;
/* FIXME: also skip adapters where DHCP is disabled */
size = 256;
if (!(buf = malloc( size ))) goto done;
err = DhcpRequestParams( DHCPCAPI_REQUEST_SYNCHRONOUS, NULL, name, NULL, send_params, recv_params,
buf, &size, NULL );
while (err == ERROR_MORE_DATA)
{
if (!(tmp = realloc( buf, size ))) goto done;
buf = tmp;
err = DhcpRequestParams( DHCPCAPI_REQUEST_SYNCHRONOUS, NULL, name, NULL, send_params, recv_params,
buf, &size, NULL );
}
if (err == ERROR_SUCCESS && param.nBytesData)
{
int len = MultiByteToWideChar( CP_ACP, 0, (const char *)param.Data, param.nBytesData, NULL, 0 );
if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
{
MultiByteToWideChar( CP_ACP, 0, (const char *)param.Data, param.nBytesData, ret, len );
ret[len] = 0;
}
TRACE("returning %s\n", debugstr_w(ret));
break;
}
}
done:
free( buf );
free( adapters );
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 = malloc( size ))) return NULL;
if (!GetComputerNameExA( format, ret, &size ))
{
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 (!stricmp( domain + len_domain - len_suffix, suffix )) return TRUE;
return FALSE;
}
static int reverse_lookup( const struct addrinfo *ai, char *hostname, size_t len )
{
return getnameinfo( ai->ai_addr, ai->ai_addrlen, hostname, len, NULL, 0, 0 );
}
static WCHAR *build_wpad_url( const char *hostname, const struct addrinfo *ai )
{
char name[NI_MAXHOST];
WCHAR *ret, *p;
int len;
while (ai && ai->ai_family != AF_INET && ai->ai_family != AF_INET6) ai = ai->ai_next;
if (!ai) return NULL;
if (!reverse_lookup( ai, name, sizeof(name) )) hostname = name;
len = lstrlenW( L"http://" ) + strlen( hostname ) + lstrlenW( L"/wpad.dat" );
if (!(ret = p = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) return NULL;
lstrcpyW( p, L"http://" );
p += lstrlenW( L"http://" );
while (*hostname) { *p++ = *hostname++; }
lstrcpyW( p, L"/wpad.dat" );
return ret;
}
static WCHAR *detect_proxy_autoconfig_url_dns(void)
{
char *fqdn, *domain, *p;
WCHAR *ret;
if (!(fqdn = get_computer_name( ComputerNamePhysicalDnsFullyQualified ))) return NULL;
if (!(domain = get_computer_name( ComputerNamePhysicalDnsDomain )))
{
free( fqdn );
return NULL;
}
p = fqdn;
while ((p = strchr( p, '.' )) && is_domain_suffix( p + 1, domain ))
{
char *name;
struct addrinfo *ai, hints;
int res;
if (!(name = malloc( sizeof("wpad") + strlen(p) )))
{
free( fqdn );
free( domain );
return NULL;
}
strcpy( name, "wpad" );
strcat( name, p );
memset( &hints, 0, sizeof(hints) );
hints.ai_flags = AI_ALL | AI_DNS_ONLY;
hints.ai_family = AF_UNSPEC;
res = getaddrinfo( name, NULL, &hints, &ai );
if (!res)
{
ret = build_wpad_url( name, ai );
freeaddrinfo( ai );
if (ret)
{
TRACE("returning %s\n", debugstr_w(ret));
free( name );
break;
}
}
free( name );
p++;
}
free( domain );
free( fqdn );
return ret;
}
static WCHAR *get_proxy_autoconfig_url(void)
{
WCHAR *ret = detect_proxy_autoconfig_url_dhcp();
if (!ret) ret = detect_proxy_autoconfig_url_dns();
return ret;
}
static WCHAR *copy_optionW(WCHAR *value)
{
DWORD len;
void *tmp;
if (!value)
return NULL;
len = (wcslen(value) + 1) * sizeof(WCHAR);
if (!(tmp = HeapAlloc(GetProcessHeap(), 0, len)))
return NULL;
return memcpy(tmp, value, len);
}
static char *copy_optionA(WCHAR *value)
{
DWORD len;
void *tmp;
if (!value)
return NULL;
len = wcslen(value) * 3 + 1;
if (!(tmp = HeapAlloc(GetProcessHeap(), 0, len)))
return NULL;
WideCharToMultiByte(CP_ACP, 0, value, -1, tmp, len, NULL, NULL);
return tmp;
}
static DWORD query_global_option(DWORD option, void *buffer, DWORD *size, BOOL unicode)
{
/* FIXME: This function currently handles more options than it should. Options requiring
* proper handles should be moved to proper functions */
switch(option) {
case INTERNET_OPTION_HTTP_VERSION:
if (*size < sizeof(HTTP_VERSION_INFO))
return ERROR_INSUFFICIENT_BUFFER;
/*
* Presently hardcoded to 1.1
*/
((HTTP_VERSION_INFO*)buffer)->dwMajorVersion = 1;
((HTTP_VERSION_INFO*)buffer)->dwMinorVersion = 1;
*size = sizeof(HTTP_VERSION_INFO);
return ERROR_SUCCESS;
case INTERNET_OPTION_CONNECTED_STATE:
FIXME("INTERNET_OPTION_CONNECTED_STATE: semi-stub\n");
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
*(ULONG*)buffer = INTERNET_STATE_CONNECTED;
*size = sizeof(ULONG);
return ERROR_SUCCESS;
case INTERNET_OPTION_PROXY: {
appinfo_t ai;
BOOL ret;
TRACE("Getting global proxy info\n");
memset(&ai, 0, sizeof(appinfo_t));
INTERNET_ConfigureProxy(&ai);
ret = APPINFO_QueryOption(&ai.hdr, INTERNET_OPTION_PROXY, buffer, size, unicode); /* FIXME */
APPINFO_Destroy(&ai.hdr);
return ret;
}
case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER\n");
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
*(ULONG*)buffer = max_conns;
*size = sizeof(ULONG);
return ERROR_SUCCESS;
case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
TRACE("INTERNET_OPTION_MAX_CONNS_1_0_SERVER\n");
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
*(ULONG*)buffer = max_1_0_conns;
*size = sizeof(ULONG);
return ERROR_SUCCESS;
case INTERNET_OPTION_SECURITY_FLAGS:
FIXME("INTERNET_OPTION_SECURITY_FLAGS: Stub\n");
return ERROR_SUCCESS;
case INTERNET_OPTION_VERSION: {
static const INTERNET_VERSION_INFO info = { 1, 2 };
TRACE("INTERNET_OPTION_VERSION\n");
if (*size < sizeof(INTERNET_VERSION_INFO))
return ERROR_INSUFFICIENT_BUFFER;
memcpy(buffer, &info, sizeof(info));
*size = sizeof(info);
return ERROR_SUCCESS;
}
case INTERNET_OPTION_PER_CONNECTION_OPTION: {
WCHAR *url;
INTERNET_PER_CONN_OPTION_LISTW *con = buffer;
INTERNET_PER_CONN_OPTION_LISTA *conA = buffer;
DWORD res = ERROR_SUCCESS, i;
proxyinfo_t pi;
LONG ret;
TRACE("Getting global proxy info\n");
if((ret = INTERNET_LoadProxySettings(&pi)))
return ret;
FIXME("INTERNET_OPTION_PER_CONNECTION_OPTION stub\n");
if (*size < sizeof(INTERNET_PER_CONN_OPTION_LISTW)) {
FreeProxyInfo(&pi);
return ERROR_INSUFFICIENT_BUFFER;
}
url = get_proxy_autoconfig_url();
for (i = 0; i < con->dwOptionCount; i++) {
INTERNET_PER_CONN_OPTIONW *optionW = con->pOptions + i;
INTERNET_PER_CONN_OPTIONA *optionA = conA->pOptions + i;
switch (optionW->dwOption) {
case INTERNET_PER_CONN_FLAGS:
if(pi.proxyEnabled)
optionW->Value.dwValue = PROXY_TYPE_PROXY;
else
optionW->Value.dwValue = PROXY_TYPE_DIRECT;
if (url)
/* native includes PROXY_TYPE_DIRECT even if PROXY_TYPE_PROXY is set */
optionW->Value.dwValue |= PROXY_TYPE_DIRECT|PROXY_TYPE_AUTO_PROXY_URL;
break;
case INTERNET_PER_CONN_PROXY_SERVER:
if (unicode)
optionW->Value.pszValue = copy_optionW(pi.proxy);
else
optionA->Value.pszValue = copy_optionA(pi.proxy);
break;
case INTERNET_PER_CONN_PROXY_BYPASS:
if (unicode)
optionW->Value.pszValue = copy_optionW(pi.proxyBypass);
else
optionA->Value.pszValue = copy_optionA(pi.proxyBypass);
break;
case INTERNET_PER_CONN_AUTOCONFIG_URL:
if (!url)
optionW->Value.pszValue = NULL;
else if (unicode)
optionW->Value.pszValue = copy_optionW(url);
else
optionA->Value.pszValue = copy_optionA(url);
break;
case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
optionW->Value.dwValue = AUTO_PROXY_FLAG_ALWAYS_DETECT;
break;
case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
FIXME("Unhandled dwOption %ld\n", optionW->dwOption);
memset(&optionW->Value, 0, sizeof(optionW->Value));
break;
default:
FIXME("Unknown dwOption %ld\n", optionW->dwOption);
res = ERROR_INVALID_PARAMETER;
break;
}
}
free(url);
FreeProxyInfo(&pi);
return res;
}
case INTERNET_OPTION_REQUEST_FLAGS:
case INTERNET_OPTION_USER_AGENT:
*size = 0;
return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
case INTERNET_OPTION_POLICY:
return ERROR_INVALID_PARAMETER;
case INTERNET_OPTION_CONNECT_TIMEOUT:
TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
*(ULONG*)buffer = connect_timeout;
*size = sizeof(ULONG);
return ERROR_SUCCESS;
}
FIXME("Stub for %ld\n", option);
return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
}
DWORD INET_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
{
switch(option) {
case INTERNET_OPTION_CONTEXT_VALUE:
if (!size)
return ERROR_INVALID_PARAMETER;
if (*size < sizeof(DWORD_PTR)) {
*size = sizeof(DWORD_PTR);
return ERROR_INSUFFICIENT_BUFFER;
}
if (!buffer)
return ERROR_INVALID_PARAMETER;
*(DWORD_PTR *)buffer = hdr->dwContext;
*size = sizeof(DWORD_PTR);
return ERROR_SUCCESS;
case INTERNET_OPTION_REQUEST_FLAGS:
WARN("INTERNET_OPTION_REQUEST_FLAGS\n");
*size = sizeof(DWORD);
return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
WARN("Called on global option %lu\n", option);
return ERROR_INTERNET_INVALID_OPERATION;
}
/* FIXME: we shouldn't call it here */
return query_global_option(option, buffer, size, unicode);
}
/***********************************************************************
* InternetQueryOptionW (WININET.@)
*
* Queries an options on the specified handle
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
LPVOID lpBuffer, LPDWORD lpdwBufferLength)
{
object_header_t *hdr;
DWORD res = ERROR_INVALID_HANDLE;
TRACE("%p %ld %p %p\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
if(hInternet) {
hdr = get_handle_object(hInternet);
if (hdr) {
res = hdr->vtbl->QueryOption(hdr, dwOption, lpBuffer, lpdwBufferLength, TRUE);
WININET_Release(hdr);
}
}else {
res = query_global_option(dwOption, lpBuffer, lpdwBufferLength, TRUE);
}
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
/***********************************************************************
* InternetQueryOptionA (WININET.@)
*
* Queries an options on the specified handle
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
LPVOID lpBuffer, LPDWORD lpdwBufferLength)
{
object_header_t *hdr;
DWORD res = ERROR_INVALID_HANDLE;
TRACE("%p %ld %p %p\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
if(hInternet) {
hdr = get_handle_object(hInternet);
if (hdr) {
res = hdr->vtbl->QueryOption(hdr, dwOption, lpBuffer, lpdwBufferLength, FALSE);
WININET_Release(hdr);
}
}else {
res = query_global_option(dwOption, lpBuffer, lpdwBufferLength, FALSE);
}
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
DWORD INET_SetOption(object_header_t *hdr, DWORD option, void *buf, DWORD size)
{
switch(option) {
case INTERNET_OPTION_SETTINGS_CHANGED:
FIXME("INTERNETOPTION_SETTINGS_CHANGED semi-stub\n");
collect_connections(COLLECT_CONNECTIONS);
return ERROR_SUCCESS;
case INTERNET_OPTION_CALLBACK:
WARN("Not settable option %lu\n", option);
return ERROR_INTERNET_OPTION_NOT_SETTABLE;
case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
WARN("Called on global option %lu\n", option);
return ERROR_INTERNET_INVALID_OPERATION;
case INTERNET_OPTION_REFRESH:
return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
}
return ERROR_INTERNET_INVALID_OPTION;
}
static DWORD set_global_option(DWORD option, void *buf, DWORD size)
{
switch(option) {
case INTERNET_OPTION_CALLBACK:
WARN("Not global option %lu\n", option);
return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER\n");
if(size != sizeof(max_conns))
return ERROR_INTERNET_BAD_OPTION_LENGTH;
if(!*(ULONG*)buf)
return ERROR_BAD_ARGUMENTS;
max_conns = *(ULONG*)buf;
return ERROR_SUCCESS;
case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
TRACE("INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER\n");
if(size != sizeof(max_1_0_conns))
return ERROR_INTERNET_BAD_OPTION_LENGTH;
if(!*(ULONG*)buf)
return ERROR_BAD_ARGUMENTS;
max_1_0_conns = *(ULONG*)buf;
return ERROR_SUCCESS;
case INTERNET_OPTION_CONNECT_TIMEOUT:
TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
if(size != sizeof(connect_timeout))
return ERROR_INTERNET_BAD_OPTION_LENGTH;
if(!*(ULONG*)buf)
return ERROR_BAD_ARGUMENTS;
connect_timeout = *(ULONG*)buf;
return ERROR_SUCCESS;
case INTERNET_OPTION_SUPPRESS_BEHAVIOR:
FIXME("INTERNET_OPTION_SUPPRESS_BEHAVIOR stub\n");
if(size != sizeof(ULONG))
return ERROR_INTERNET_BAD_OPTION_LENGTH;
FIXME("%08lx\n", *(ULONG*)buf);
return ERROR_SUCCESS;
}
return INET_SetOption(NULL, option, buf, size);
}
/***********************************************************************
* InternetSetOptionW (WININET.@)
*
* Sets an options on the specified handle
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
LPVOID lpBuffer, DWORD dwBufferLength)
{
object_header_t *lpwhh;
BOOL ret = TRUE;
DWORD res;
TRACE("(%p %ld %p %ld)\n", hInternet, dwOption, lpBuffer, dwBufferLength);
lpwhh = (object_header_t*) get_handle_object( hInternet );
if(lpwhh)
res = lpwhh->vtbl->SetOption(lpwhh, dwOption, lpBuffer, dwBufferLength);
else
res = set_global_option(dwOption, lpBuffer, dwBufferLength);
if(res != ERROR_INTERNET_INVALID_OPTION) {
if(lpwhh)
WININET_Release(lpwhh);
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
switch (dwOption)
{
case INTERNET_OPTION_HTTP_VERSION:
{
HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
FIXME("Option INTERNET_OPTION_HTTP_VERSION(%ld,%ld): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
}
break;
case INTERNET_OPTION_ERROR_MASK:
{
if(!lpwhh) {
SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
return FALSE;
} else if(*(ULONG*)lpBuffer & (~(INTERNET_ERROR_MASK_INSERT_CDROM|
INTERNET_ERROR_MASK_COMBINED_SEC_CERT|
INTERNET_ERROR_MASK_LOGIN_FAILURE_DISPLAY_ENTITY_BODY))) {
SetLastError(ERROR_INVALID_PARAMETER);
ret = FALSE;
} else if(dwBufferLength != sizeof(ULONG)) {
SetLastError(ERROR_INTERNET_BAD_OPTION_LENGTH);
ret = FALSE;
} else {
TRACE("INTERNET_OPTION_ERROR_MASK: %lx\n", *(ULONG*)lpBuffer);
lpwhh->ErrorMask = *(ULONG*)lpBuffer;
}
}
break;
case INTERNET_OPTION_PROXY:
{
INTERNET_PROXY_INFOW *info = lpBuffer;
if (!lpBuffer || dwBufferLength < sizeof(INTERNET_PROXY_INFOW))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!hInternet)
{
EnterCriticalSection( &WININET_cs );
free_global_proxy();
global_proxy = malloc(sizeof(proxyinfo_t));
if (global_proxy)
{
if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY)
{
global_proxy->proxyEnabled = 1;
global_proxy->proxy = wcsdup(info->lpszProxy);
global_proxy->proxyBypass = wcsdup(info->lpszProxyBypass);
}
else
{
global_proxy->proxyEnabled = 0;
global_proxy->proxy = global_proxy->proxyBypass = NULL;
}
}
LeaveCriticalSection( &WININET_cs );
}
else
{
/* In general, each type of object should handle
* INTERNET_OPTION_PROXY directly. This FIXME ensures it doesn't
* get silently dropped.
*/
FIXME("INTERNET_OPTION_PROXY unimplemented\n");
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
}
break;
}
case INTERNET_OPTION_CODEPAGE:
{
ULONG codepage = *(ULONG *)lpBuffer;
FIXME("Option INTERNET_OPTION_CODEPAGE (%ld): STUB\n", codepage);
}
break;
case INTERNET_OPTION_REQUEST_PRIORITY:
{
ULONG priority = *(ULONG *)lpBuffer;
FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%ld): STUB\n", priority);
}
break;
case INTERNET_OPTION_CONNECT_TIMEOUT:
{
ULONG connecttimeout = *(ULONG *)lpBuffer;
FIXME("Option INTERNET_OPTION_CONNECT_TIMEOUT (%ld): STUB\n", connecttimeout);
}
break;
case INTERNET_OPTION_DATA_RECEIVE_TIMEOUT:
{
ULONG receivetimeout = *(ULONG *)lpBuffer;
FIXME("Option INTERNET_OPTION_DATA_RECEIVE_TIMEOUT (%ld): STUB\n", receivetimeout);
}
break;
case INTERNET_OPTION_RESET_URLCACHE_SESSION:
FIXME("Option INTERNET_OPTION_RESET_URLCACHE_SESSION: STUB\n");
break;
case INTERNET_OPTION_END_BROWSER_SESSION:
FIXME("Option INTERNET_OPTION_END_BROWSER_SESSION: semi-stub\n");
free_cookie();
free_authorization_cache();
break;
case INTERNET_OPTION_CONNECTED_STATE:
FIXME("Option INTERNET_OPTION_CONNECTED_STATE: STUB\n");
break;
case INTERNET_OPTION_DISABLE_PASSPORT_AUTH:
TRACE("Option INTERNET_OPTION_DISABLE_PASSPORT_AUTH: harmless stub, since not enabled\n");
break;
case INTERNET_OPTION_IGNORE_OFFLINE:
FIXME("Option INTERNET_OPTION_IGNORE_OFFLINE: STUB\n");
break;
case INTERNET_OPTION_SEND_TIMEOUT:
case INTERNET_OPTION_RECEIVE_TIMEOUT:
case INTERNET_OPTION_DATA_SEND_TIMEOUT:
{
ULONG timeout = *(ULONG *)lpBuffer;
FIXME("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT/DATA_SEND_TIMEOUT %ld\n", timeout);
break;
}
case INTERNET_OPTION_CONNECT_RETRIES:
{
ULONG retries = *(ULONG *)lpBuffer;
FIXME("INTERNET_OPTION_CONNECT_RETRIES %ld\n", retries);
break;
}
case INTERNET_OPTION_CONTEXT_VALUE:
{
if (!lpwhh)
{
SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
return FALSE;
}
if (!lpBuffer || dwBufferLength != sizeof(DWORD_PTR))
{
SetLastError(ERROR_INVALID_PARAMETER);
ret = FALSE;
}
else
lpwhh->dwContext = *(DWORD_PTR *)lpBuffer;
break;
}
case INTERNET_OPTION_SECURITY_FLAGS:
FIXME("Option INTERNET_OPTION_SECURITY_FLAGS; STUB\n");
break;
case INTERNET_OPTION_DISABLE_AUTODIAL:
FIXME("Option INTERNET_OPTION_DISABLE_AUTODIAL; STUB\n");
break;
case INTERNET_OPTION_HTTP_DECODING:
if (!lpwhh)
{
SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
return FALSE;
}
if (!lpBuffer || dwBufferLength != sizeof(BOOL))
{
SetLastError(ERROR_INVALID_PARAMETER);
ret = FALSE;
}
else
lpwhh->decoding = *(BOOL *)lpBuffer;
break;
case INTERNET_OPTION_COOKIES_3RD_PARTY:
FIXME("INTERNET_OPTION_COOKIES_3RD_PARTY; STUB\n");
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
break;
case INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY:
FIXME("INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY; STUB\n");
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
break;
case INTERNET_OPTION_CODEPAGE_PATH:
FIXME("INTERNET_OPTION_CODEPAGE_PATH; STUB\n");
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
break;
case INTERNET_OPTION_CODEPAGE_EXTRA:
FIXME("INTERNET_OPTION_CODEPAGE_EXTRA; STUB\n");
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
break;
case INTERNET_OPTION_IDN:
FIXME("INTERNET_OPTION_IDN; STUB\n");
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
break;
case INTERNET_OPTION_POLICY:
SetLastError(ERROR_INVALID_PARAMETER);
ret = FALSE;
break;
case INTERNET_OPTION_PER_CONNECTION_OPTION: {
INTERNET_PER_CONN_OPTION_LISTW *con = lpBuffer;
LONG res;
unsigned int i;
proxyinfo_t pi;
if (INTERNET_LoadProxySettings(&pi)) return FALSE;
for (i = 0; i < con->dwOptionCount; i++) {
INTERNET_PER_CONN_OPTIONW *option = con->pOptions + i;
switch (option->dwOption) {
case INTERNET_PER_CONN_PROXY_SERVER:
free(pi.proxy);
pi.proxy = wcsdup(option->Value.pszValue);
break;
case INTERNET_PER_CONN_FLAGS:
if(option->Value.dwValue & PROXY_TYPE_PROXY)
pi.proxyEnabled = 1;
else
{
if(option->Value.dwValue != PROXY_TYPE_DIRECT)
FIXME("Unhandled flags: 0x%lx\n", option->Value.dwValue);
pi.proxyEnabled = 0;
}
break;
case INTERNET_PER_CONN_PROXY_BYPASS:
free(pi.proxyBypass);
pi.proxyBypass = wcsdup(option->Value.pszValue);
break;
case INTERNET_PER_CONN_AUTOCONFIG_URL:
case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
FIXME("Unhandled dwOption %ld\n", option->dwOption);
break;
default:
FIXME("Unknown dwOption %ld\n", option->dwOption);
SetLastError(ERROR_INVALID_PARAMETER);
break;
}
}
if ((res = INTERNET_SaveProxySettings(&pi)))
SetLastError(res);
FreeProxyInfo(&pi);
ret = (res == ERROR_SUCCESS);
break;
}
default:
FIXME("Option %ld STUB\n",dwOption);
SetLastError(ERROR_INTERNET_INVALID_OPTION);
ret = FALSE;
break;
}
if(lpwhh)
WININET_Release( lpwhh );
return ret;
}
/***********************************************************************
* InternetSetOptionA (WININET.@)
*
* Sets an options on the specified handle.
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
LPVOID lpBuffer, DWORD dwBufferLength)
{
LPVOID wbuffer;
DWORD wlen;
BOOL r;
switch( dwOption )
{
case INTERNET_OPTION_PROXY:
{
LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
LPINTERNET_PROXY_INFOW piw;
DWORD proxlen, prbylen;
LPWSTR prox, prby;
proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
wlen = sizeof(*piw) + proxlen + prbylen;
wbuffer = malloc( wlen * sizeof(WCHAR) );
piw = (LPINTERNET_PROXY_INFOW) wbuffer;
piw->dwAccessType = pi->dwAccessType;
prox = (LPWSTR) &piw[1];
prby = &prox[proxlen+1];
MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
piw->lpszProxy = prox;
piw->lpszProxyBypass = prby;
}
break;
case INTERNET_OPTION_USER_AGENT:
case INTERNET_OPTION_USERNAME:
case INTERNET_OPTION_PASSWORD:
case INTERNET_OPTION_PROXY_USERNAME:
case INTERNET_OPTION_PROXY_PASSWORD:
wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 );
if (!(wbuffer = malloc( wlen * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, wbuffer, wlen );
break;
case INTERNET_OPTION_PER_CONNECTION_OPTION: {
unsigned int i;
INTERNET_PER_CONN_OPTION_LISTW *listW;
INTERNET_PER_CONN_OPTION_LISTA *listA = lpBuffer;
wlen = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
wbuffer = malloc( wlen );
listW = wbuffer;
listW->dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
if (listA->pszConnection)
{
wlen = MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, NULL, 0 );
listW->pszConnection = malloc( wlen * sizeof(WCHAR) );
MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, listW->pszConnection, wlen );
}
else
listW->pszConnection = NULL;
listW->dwOptionCount = listA->dwOptionCount;
listW->dwOptionError = listA->dwOptionError;
listW->pOptions = malloc( sizeof(INTERNET_PER_CONN_OPTIONW) * listA->dwOptionCount );
for (i = 0; i < listA->dwOptionCount; ++i) {
INTERNET_PER_CONN_OPTIONA *optA = listA->pOptions + i;
INTERNET_PER_CONN_OPTIONW *optW = listW->pOptions + i;
optW->dwOption = optA->dwOption;
switch (optA->dwOption) {
case INTERNET_PER_CONN_AUTOCONFIG_URL:
case INTERNET_PER_CONN_PROXY_BYPASS:
case INTERNET_PER_CONN_PROXY_SERVER:
case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
if (optA->Value.pszValue)
{
wlen = MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, NULL, 0 );
optW->Value.pszValue = malloc( wlen * sizeof(WCHAR) );
MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, optW->Value.pszValue, wlen );
}
else
optW->Value.pszValue = NULL;
break;
case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
case INTERNET_PER_CONN_FLAGS:
case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
optW->Value.dwValue = optA->Value.dwValue;
break;
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
optW->Value.ftValue = optA->Value.ftValue;
break;
default:
WARN("Unknown PER_CONN dwOption: %ld, guessing at conversion to Wide\n", optA->dwOption);
optW->Value.dwValue = optA->Value.dwValue;
break;
}
}
}
break;
default:
wbuffer = lpBuffer;
wlen = dwBufferLength;
}
r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
if( lpBuffer != wbuffer )
{
if (dwOption == INTERNET_OPTION_PER_CONNECTION_OPTION)
{
INTERNET_PER_CONN_OPTION_LISTW *list = wbuffer;
unsigned int i;
for (i = 0; i < list->dwOptionCount; ++i) {
INTERNET_PER_CONN_OPTIONW *opt = list->pOptions + i;
switch (opt->dwOption) {
case INTERNET_PER_CONN_AUTOCONFIG_URL:
case INTERNET_PER_CONN_PROXY_BYPASS:
case INTERNET_PER_CONN_PROXY_SERVER:
case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
free( opt->Value.pszValue );
break;
default:
break;
}
}
free( list->pOptions );
}
free( wbuffer );
}
return r;
}
/***********************************************************************
* InternetSetOptionExA (WININET.@)
*/
BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
{
FIXME("Flags %08lx ignored\n", dwFlags);
return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
}
/***********************************************************************
* InternetSetOptionExW (WININET.@)
*/
BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
{
FIXME("Flags %08lx ignored\n", dwFlags);
if( dwFlags & ~ISO_VALID_FLAGS )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
}
static const WCHAR WININET_wkday[7][4] =
{ L"Sun", L"Mon", L"Tue", L"Wed",
L"Thu", L"Fri", L"Sat"};
static const WCHAR WININET_month[12][4] =
{ L"Jan", L"Feb", L"Mar", L"Apr",
L"May", L"Jun", L"Jul", L"Aug",
L"Sep", L"Oct", L"Nov", L"Dec"};
static inline BOOL is_time_digit(const WCHAR c)
{
return c >= '0' && c <= '9';
}
/***********************************************************************
* InternetTimeFromSystemTimeA (WININET.@)
*/
BOOL WINAPI InternetTimeFromSystemTimeA( const SYSTEMTIME* time, DWORD format, LPSTR string, DWORD size )
{
BOOL ret;
WCHAR stringW[INTERNET_RFC1123_BUFSIZE];
TRACE( "%p 0x%08lx %p 0x%08lx\n", time, format, string, size );
if (!time || !string || format != INTERNET_RFC1123_FORMAT)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (size < INTERNET_RFC1123_BUFSIZE * sizeof(*string))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
ret = InternetTimeFromSystemTimeW( time, format, stringW, sizeof(stringW) );
if (ret) WideCharToMultiByte( CP_ACP, 0, stringW, -1, string, size, NULL, NULL );
return ret;
}
/***********************************************************************
* InternetTimeFromSystemTimeW (WININET.@)
*/
BOOL WINAPI InternetTimeFromSystemTimeW( const SYSTEMTIME* time, DWORD format, LPWSTR string, DWORD size )
{
TRACE( "%p 0x%08lx %p 0x%08lx\n", time, format, string, size );
if (!time || !string || format != INTERNET_RFC1123_FORMAT)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (size < INTERNET_RFC1123_BUFSIZE * sizeof(*string))
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
swprintf( string, size, L"%s, %02d %s %4d %02d:%02d:%02d GMT",
WININET_wkday[time->wDayOfWeek],
time->wDay,
WININET_month[time->wMonth - 1],
time->wYear,
time->wHour,
time->wMinute,
time->wSecond );
return TRUE;
}
/***********************************************************************
* InternetTimeToSystemTimeA (WININET.@)
*/
BOOL WINAPI InternetTimeToSystemTimeA( LPCSTR string, SYSTEMTIME* time, DWORD reserved )
{
BOOL ret = FALSE;
WCHAR *stringW;
TRACE( "%s %p 0x%08lx\n", debugstr_a(string), time, reserved );
stringW = strdupAtoW( string );
if (stringW)
{
ret = InternetTimeToSystemTimeW( stringW, time, reserved );
free( stringW );
}
return ret;
}
/***********************************************************************
* InternetTimeToSystemTimeW (WININET.@)
*/
BOOL WINAPI InternetTimeToSystemTimeW( LPCWSTR string, SYSTEMTIME* time, DWORD reserved )
{
unsigned int i;
const WCHAR *s = string;
WCHAR *end;
TRACE( "%s %p 0x%08lx\n", debugstr_w(string), time, reserved );
if (!string || !time) return FALSE;
/* Windows does this too */
GetSystemTime( time );
/* Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
* a SYSTEMTIME structure.
*/
while (*s && !iswalpha(*s) && !is_time_digit(*s)) s++;
if (*s == '\0') return TRUE;
time->wDayOfWeek = 7;
if (iswalpha(*s))
{
if (s[1] == '\0' || s[2] == '\0') return TRUE;
for (i = 0; i < 7; i++)
{
if (!wcsnicmp(WININET_wkday[i], s, 3))
{
time->wDayOfWeek = i;
break;
}
}
}
else if (is_time_digit(*s))
{
time->wDayOfWeek = wcstol(s, &end, 10);
s = end;
}
if (time->wDayOfWeek > 6) return TRUE;
while (*s && !is_time_digit(*s)) s++;
time->wDay = wcstol( s, &end, 10 );
s = end;
while (*s && !iswalpha(*s) && !is_time_digit(*s)) s++;
if (*s == '\0') return TRUE;
time->wMonth = 0;
if (iswalpha(*s))
{
if (s[1] == '\0' || s[2] == '\0') return TRUE;
for (i = 0; i < 12; i++)
{
if (!wcsnicmp(WININET_month[i], s, 3))
{
time->wMonth = i + 1;
break;
}
}
}
else if (is_time_digit(*s))
{
time->wMonth = wcstol(s, &end, 10);
s = end;
}
if (time->wMonth == 0) return TRUE;
while (*s && !is_time_digit(*s)) s++;
if (*s == '\0') return TRUE;
time->wYear = wcstol( s, &end, 10 );
s = end;
while (*s && !is_time_digit(*s)) s++;
if (*s == '\0') return TRUE;
time->wHour = wcstol( s, &end, 10 );
s = end;
while (*s && !is_time_digit(*s)) s++;
if (*s == '\0') return TRUE;
time->wMinute = wcstol( s, &end, 10 );
s = end;
while (*s && !is_time_digit(*s)) s++;
if (*s == '\0') return TRUE;
time->wSecond = wcstol( s, &end, 10 );
s = end;
time->wMilliseconds = 0;
return TRUE;
}
/***********************************************************************
* InternetCheckConnectionW (WININET.@)
*
* Pings a requested host to check internet connection
*
* RETURNS
* TRUE on success and FALSE on failure. If a failure then
* ERROR_NOT_CONNECTED is placed into GetLastError
*
*/
BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
{
/*
* this is a kludge which runs the resident ping program and reads the output.
*
* Anyone have a better idea?
*/
BOOL rc = FALSE;
static const CHAR ping[] = "ping -c 1 ";
static const CHAR redirect[] = " >/dev/null 2>/dev/null";
WCHAR *host;
DWORD len, host_len;
INTERNET_PORT port;
int status = -1;
FIXME("(%s %lx %lx)\n", debugstr_w(lpszUrl), dwFlags, dwReserved);
/*
* Crack or set the Address
*/
if (lpszUrl == NULL)
{
/*
* According to the doc we are supposed to use the ip for the next
* server in the WnInet internal server database. I have
* no idea what that is or how to get it.
*
* So someone needs to implement this.
*/
FIXME("Unimplemented with URL of NULL\n");
return TRUE;
}
else
{
URL_COMPONENTSW components = {sizeof(components)};
components.dwHostNameLength = 1;
if (!InternetCrackUrlW(lpszUrl,0,0,&components))
goto End;
host = components.lpszHostName;
host_len = components.dwHostNameLength;
port = components.nPort;
TRACE("host name: %s port: %d\n",debugstr_wn(host, host_len), port);
}
if (dwFlags & FLAG_ICC_FORCE_CONNECTION)
{
struct sockaddr_storage saddr;
int sa_len = sizeof(saddr);
WCHAR *host_z;
int fd;
BOOL b;
host_z = strndupW(host, host_len);
if (!host_z)
return FALSE;
b = GetAddress(host_z, port, (struct sockaddr *)&saddr, &sa_len, NULL);
free(host_z);
if(!b)
goto End;
init_winsock();
fd = socket(saddr.ss_family, SOCK_STREAM, 0);
if (fd != -1)
{
if (connect(fd, (struct sockaddr *)&saddr, sa_len) == 0)
rc = TRUE;
closesocket(fd);
}
}
else
{
/*
* Build our ping command
*/
char *command;
len = WideCharToMultiByte(CP_UNIXCP, 0, host, host_len, NULL, 0, NULL, NULL);
command = malloc(strlen(ping) + len + strlen(redirect) + 1);
strcpy(command, ping);
WideCharToMultiByte(CP_UNIXCP, 0, host, host_len, command+sizeof(ping)-1, len, NULL, NULL);
strcpy(command+sizeof(ping)-1+len, redirect);
TRACE("Ping command is : %s\n",command);
status = system(command);
free(command);
TRACE("Ping returned a code of %i\n",status);
/* Ping return code of 0 indicates success */
if (status == 0)
rc = TRUE;
}
End:
if (rc == FALSE)
INTERNET_SetLastError(ERROR_NOT_CONNECTED);
return rc;
}
/***********************************************************************
* InternetCheckConnectionA (WININET.@)
*
* Pings a requested host to check internet connection
*
* RETURNS
* TRUE on success and FALSE on failure. If a failure then
* ERROR_NOT_CONNECTED is placed into GetLastError
*
*/
BOOL WINAPI InternetCheckConnectionA(LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
{
WCHAR *url = NULL;
BOOL rc;
if(lpszUrl) {
url = strdupAtoW(lpszUrl);
if(!url)
return FALSE;
}
rc = InternetCheckConnectionW(url, dwFlags, dwReserved);
free(url);
return rc;
}
/**********************************************************
* INTERNET_InternetOpenUrlW (internal)
*
* Opens an URL
*
* RETURNS
* handle of connection or NULL on failure
*/
static HINTERNET INTERNET_InternetOpenUrlW(appinfo_t *hIC, LPCWSTR lpszUrl,
LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
{
URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
WCHAR *host, *user = NULL, *pass = NULL, *path;
HINTERNET client = NULL, client1 = NULL;
DWORD res;
TRACE("(%p, %s, %s, %08lx, %08lx, %08Ix)\n", hIC, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
dwHeadersLength, dwFlags, dwContext);
urlComponents.dwHostNameLength = 1;
urlComponents.dwUserNameLength = 1;
urlComponents.dwPasswordLength = 1;
urlComponents.dwUrlPathLength = 1;
urlComponents.dwExtraInfoLength = 1;
if(!InternetCrackUrlW(lpszUrl, lstrlenW(lpszUrl), 0, &urlComponents))
return NULL;
if ((urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS) &&
urlComponents.dwExtraInfoLength)
{
assert(urlComponents.lpszUrlPath + urlComponents.dwUrlPathLength == urlComponents.lpszExtraInfo);
urlComponents.dwUrlPathLength += urlComponents.dwExtraInfoLength;
}
host = strndupW(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
path = strndupW(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength);
if(urlComponents.dwUserNameLength)
user = strndupW(urlComponents.lpszUserName, urlComponents.dwUserNameLength);
if(urlComponents.dwPasswordLength)
pass = strndupW(urlComponents.lpszPassword, urlComponents.dwPasswordLength);
switch(urlComponents.nScheme) {
case INTERNET_SCHEME_FTP:
client = FTP_Connect(hIC, host, urlComponents.nPort,
user, pass, dwFlags, dwContext, INET_OPENURL);
if(client == NULL)
break;
client1 = FtpOpenFileW(client, path, GENERIC_READ, dwFlags, dwContext);
if(client1 == NULL) {
InternetCloseHandle(client);
break;
}
break;
case INTERNET_SCHEME_HTTP:
case INTERNET_SCHEME_HTTPS: {
LPCWSTR accept[2] = { L"*/*", NULL };
if (urlComponents.nScheme == INTERNET_SCHEME_HTTPS) dwFlags |= INTERNET_FLAG_SECURE;
/* FIXME: should use pointers, not handles, as handles are not thread-safe */
res = HTTP_Connect(hIC, host, urlComponents.nPort,
user, pass, dwFlags, dwContext, INET_OPENURL, &client);
if(res != ERROR_SUCCESS) {
INTERNET_SetLastError(res);
break;
}
client1 = HttpOpenRequestW(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
if(client1 == NULL) {
InternetCloseHandle(client);
break;
}
HttpAddRequestHeadersW(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
if (!HttpSendRequestW(client1, NULL, 0, NULL, 0) &&
GetLastError() != ERROR_IO_PENDING) {
InternetCloseHandle(client1);
client1 = NULL;
break;
}
}
case INTERNET_SCHEME_GOPHER:
/* gopher doesn't seem to be implemented in wine, but it's supposed
* to be supported by InternetOpenUrlA. */
default:
SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
break;
}
TRACE(" %p <--\n", client1);
free(host);
free(path);
free(user);
free(pass);
return client1;
}
/**********************************************************
* InternetOpenUrlW (WININET.@)
*
* Opens an URL
*
* RETURNS
* handle of connection or NULL on failure
*/
typedef struct {
task_header_t hdr;
WCHAR *url;
WCHAR *headers;
DWORD headers_len;
DWORD flags;
DWORD_PTR context;
} open_url_task_t;
static void AsyncInternetOpenUrlProc(task_header_t *hdr)
{
open_url_task_t *task = (open_url_task_t*)hdr;
TRACE("%p\n", task->hdr.hdr);
INTERNET_InternetOpenUrlW((appinfo_t*)task->hdr.hdr, task->url, task->headers,
task->headers_len, task->flags, task->context);
free(task->url);
free(task->headers);
}
HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
{
HINTERNET ret = NULL;
appinfo_t *hIC = NULL;
if (TRACE_ON(wininet)) {
TRACE("(%p, %s, %s, %08lx, %08lx, %08Ix)\n", hInternet, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
dwHeadersLength, dwFlags, dwContext);
TRACE(" flags :");
dump_INTERNET_FLAGS(dwFlags);
}
if (!lpszUrl)
{
SetLastError(ERROR_INVALID_PARAMETER);
goto lend;
}
hIC = (appinfo_t*)get_handle_object( hInternet );
if (NULL == hIC || hIC->hdr.htype != WH_HINIT) {
SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
goto lend;
}
if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) {
open_url_task_t *task;
task = alloc_async_task(&hIC->hdr, AsyncInternetOpenUrlProc, sizeof(*task));
task->url = wcsdup(lpszUrl);
task->headers = wcsdup(lpszHeaders);
task->headers_len = dwHeadersLength;
task->flags = dwFlags;
task->context = dwContext;
INTERNET_AsyncCall(&task->hdr);
SetLastError(ERROR_IO_PENDING);
} else {
ret = INTERNET_InternetOpenUrlW(hIC, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);
}
lend:
if( hIC )
WININET_Release( &hIC->hdr );
TRACE(" %p <--\n", ret);
return ret;
}
/**********************************************************
* InternetOpenUrlA (WININET.@)
*
* Opens an URL
*
* RETURNS
* handle of connection or NULL on failure
*/
HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
{
HINTERNET rc = NULL;
LPWSTR szUrl = NULL;
WCHAR *headers = NULL;
TRACE("\n");
if(lpszUrl) {
szUrl = strdupAtoW(lpszUrl);
if(!szUrl)
return NULL;
}
if(lpszHeaders) {
headers = strndupAtoW(lpszHeaders, dwHeadersLength, &dwHeadersLength);
if(!headers) {
free(szUrl);
return NULL;
}
}
rc = InternetOpenUrlW(hInternet, szUrl, headers, dwHeadersLength, dwFlags, dwContext);
free(szUrl);
free(headers);
return rc;
}
static LPWITHREADERROR INTERNET_AllocThreadError(void)
{
WITHREADERROR *lpwite = malloc(sizeof(*lpwite));
if (lpwite)
{
lpwite->dwError = 0;
lpwite->response[0] = '\0';
}
if (!TlsSetValue(g_dwTlsErrIndex, lpwite))
{
free(lpwite);
return NULL;
}
return lpwite;
}
/***********************************************************************
* INTERNET_SetLastError (internal)
*
* Set last thread specific error
*
* RETURNS
*
*/
void INTERNET_SetLastError(DWORD dwError)
{
LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
if (!lpwite)
lpwite = INTERNET_AllocThreadError();
SetLastError(dwError);
if(lpwite)
lpwite->dwError = dwError;
}
/***********************************************************************
* INTERNET_GetLastError (internal)
*
* Get last thread specific error
*
* RETURNS
*
*/
DWORD INTERNET_GetLastError(void)
{
LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
if (!lpwite) return 0;
/* TlsGetValue clears last error, so set it again here */
SetLastError(lpwite->dwError);
return lpwite->dwError;
}
/***********************************************************************
* INTERNET_WorkerThreadFunc (internal)
*
* Worker thread execution function
*
* RETURNS
*
*/
static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
{
task_header_t *task = lpvParam;
TRACE("\n");
task->proc(task);
WININET_Release(task->hdr);
free(task);
if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
{
free(TlsGetValue(g_dwTlsErrIndex));
TlsSetValue(g_dwTlsErrIndex, NULL);
}
return TRUE;
}
void *alloc_async_task(object_header_t *hdr, async_task_proc_t proc, size_t size)
{
task_header_t *task;
task = malloc(size);
if(!task)
return NULL;
task->hdr = WININET_AddRef(hdr);
task->proc = proc;
return task;
}
/***********************************************************************
* INTERNET_AsyncCall (internal)
*
* Retrieves work request from queue
*
* RETURNS
*
*/
DWORD INTERNET_AsyncCall(task_header_t *task)
{
BOOL bSuccess;
TRACE("\n");
bSuccess = QueueUserWorkItem(INTERNET_WorkerThreadFunc, task, WT_EXECUTELONGFUNCTION);
if (!bSuccess)
{
free(task);
return ERROR_INTERNET_ASYNC_THREAD_FAILED;
}
return ERROR_SUCCESS;
}
/***********************************************************************
* INTERNET_GetResponseBuffer (internal)
*
* RETURNS
*
*/
LPSTR INTERNET_GetResponseBuffer(void)
{
LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
if (!lpwite)
lpwite = INTERNET_AllocThreadError();
TRACE("\n");
return lpwite->response;
}
/**********************************************************
* InternetQueryDataAvailable (WININET.@)
*
* Determines how much data is available to be read.
*
* RETURNS
* TRUE on success, FALSE if an error occurred. If
* INTERNET_FLAG_ASYNC was specified in InternetOpen, and
* no data is presently available, FALSE is returned with
* the last error ERROR_IO_PENDING; a callback with status
* INTERNET_STATUS_REQUEST_COMPLETE will be sent when more
* data is available.
*/
BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
LPDWORD lpdwNumberOfBytesAvailable,
DWORD dwFlags, DWORD_PTR dwContext)
{
object_header_t *hdr;
DWORD res;
TRACE("(%p %p %lx %Ix)\n", hFile, lpdwNumberOfBytesAvailable, dwFlags, dwContext);
hdr = get_handle_object( hFile );
if (!hdr) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(hdr->vtbl->QueryDataAvailable) {
res = hdr->vtbl->QueryDataAvailable(hdr, lpdwNumberOfBytesAvailable, dwFlags, dwContext);
}else {
WARN("wrong handle\n");
res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
}
WININET_Release(hdr);
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
DWORD create_req_file(const WCHAR *file_name, req_file_t **ret)
{
req_file_t *req_file;
req_file = calloc(1, sizeof(*req_file));
if(!req_file)
return ERROR_NOT_ENOUGH_MEMORY;
req_file->ref = 1;
req_file->file_name = wcsdup(file_name);
if(!req_file->file_name) {
free(req_file);
return ERROR_NOT_ENOUGH_MEMORY;
}
req_file->file_handle = CreateFileW(req_file->file_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(req_file->file_handle == INVALID_HANDLE_VALUE) {
req_file_release(req_file);
return GetLastError();
}
*ret = req_file;
return ERROR_SUCCESS;
}
void req_file_release(req_file_t *req_file)
{
if(InterlockedDecrement(&req_file->ref))
return;
if(!req_file->is_committed)
DeleteFileW(req_file->file_name);
if(req_file->file_handle && req_file->file_handle != INVALID_HANDLE_VALUE)
CloseHandle(req_file->file_handle);
free(req_file->file_name);
free(req_file->url);
free(req_file);
}
/***********************************************************************
* InternetLockRequestFile (WININET.@)
*/
BOOL WINAPI InternetLockRequestFile(HINTERNET hInternet, HANDLE *lphLockReqHandle)
{
req_file_t *req_file = NULL;
object_header_t *hdr;
DWORD res;
TRACE("(%p %p)\n", hInternet, lphLockReqHandle);
hdr = get_handle_object(hInternet);
if (!hdr) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if(hdr->vtbl->LockRequestFile) {
res = hdr->vtbl->LockRequestFile(hdr, &req_file);
}else {
WARN("wrong handle\n");
res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
}
WININET_Release(hdr);
*lphLockReqHandle = req_file;
if(res != ERROR_SUCCESS)
SetLastError(res);
return res == ERROR_SUCCESS;
}
BOOL WINAPI InternetUnlockRequestFile(HANDLE hLockHandle)
{
TRACE("(%p)\n", hLockHandle);
req_file_release(hLockHandle);
return TRUE;
}
/***********************************************************************
* InternetAutodial (WININET.@)
*
* On windows this function is supposed to dial the default internet
* connection. We don't want to have Wine dial out to the internet so
* we return TRUE by default. It might be nice to check if we are connected.
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
{
FIXME("STUB\n");
/* Tell that we are connected to the internet. */
return TRUE;
}
/***********************************************************************
* InternetAutodialHangup (WININET.@)
*
* Hangs up a connection made with InternetAutodial
*
* PARAM
* dwReserved
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
{
FIXME("STUB\n");
/* we didn't dial, we don't disconnect */
return TRUE;
}
/***********************************************************************
* InternetCombineUrlA (WININET.@)
*
* Combine a base URL with a relative URL
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
DWORD dwFlags)
{
HRESULT hr=S_OK;
TRACE("(%s, %s, %p, %p, 0x%08lx)\n", debugstr_a(lpszBaseUrl), debugstr_a(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
/* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
dwFlags ^= ICU_NO_ENCODE;
hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
return (hr==S_OK);
}
/***********************************************************************
* InternetCombineUrlW (WININET.@)
*
* Combine a base URL with a relative URL
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
DWORD dwFlags)
{
HRESULT hr=S_OK;
TRACE("(%s, %s, %p, %p, 0x%08lx)\n", debugstr_w(lpszBaseUrl), debugstr_w(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
/* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
dwFlags ^= ICU_NO_ENCODE;
hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
return (hr==S_OK);
}
/* max port num is 65535 => 5 digits */
#define MAX_WORD_DIGITS 5
#define URL_GET_COMP_LENGTH(url, component) ((url)->dw##component##Length ? \
(url)->dw##component##Length : lstrlenW((url)->lpsz##component))
#define URL_GET_COMP_LENGTHA(url, component) ((url)->dw##component##Length ? \
(url)->dw##component##Length : strlen((url)->lpsz##component))
static BOOL url_uses_default_port(INTERNET_SCHEME nScheme, INTERNET_PORT nPort)
{
if ((nScheme == INTERNET_SCHEME_HTTP) &&
(nPort == INTERNET_DEFAULT_HTTP_PORT))
return TRUE;
if ((nScheme == INTERNET_SCHEME_HTTPS) &&
(nPort == INTERNET_DEFAULT_HTTPS_PORT))
return TRUE;
if ((nScheme == INTERNET_SCHEME_FTP) &&
(nPort == INTERNET_DEFAULT_FTP_PORT))
return TRUE;
if ((nScheme == INTERNET_SCHEME_GOPHER) &&
(nPort == INTERNET_DEFAULT_GOPHER_PORT))
return TRUE;
if (nPort == INTERNET_INVALID_PORT_NUMBER)
return TRUE;
return FALSE;
}
/* opaque urls do not fit into the standard url hierarchy and don't have
* two following slashes */
static inline BOOL scheme_is_opaque(INTERNET_SCHEME nScheme)
{
return (nScheme != INTERNET_SCHEME_FTP) &&
(nScheme != INTERNET_SCHEME_GOPHER) &&
(nScheme != INTERNET_SCHEME_HTTP) &&
(nScheme != INTERNET_SCHEME_HTTPS) &&
(nScheme != INTERNET_SCHEME_FILE);
}
static LPCWSTR INTERNET_GetSchemeString(INTERNET_SCHEME scheme)
{
int index;
if (scheme < INTERNET_SCHEME_FIRST)
return NULL;
index = scheme - INTERNET_SCHEME_FIRST;
if (index >= ARRAY_SIZE(url_schemes))
return NULL;
return url_schemes[index];
}
/* we can calculate using ansi strings because we're just
* calculating string length, not size
*/
static BOOL calc_url_length(LPURL_COMPONENTSW lpUrlComponents,
LPDWORD lpdwUrlLength)
{
INTERNET_SCHEME nScheme;
*lpdwUrlLength = 0;
if (lpUrlComponents->lpszScheme)
{
DWORD dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Scheme);
*lpdwUrlLength += dwLen;
nScheme = GetInternetSchemeW(lpUrlComponents->lpszScheme, dwLen);
}
else
{
LPCWSTR scheme;
nScheme = lpUrlComponents->nScheme;
if (nScheme == INTERNET_SCHEME_DEFAULT)
nScheme = INTERNET_SCHEME_HTTP;
scheme = INTERNET_GetSchemeString(nScheme);
*lpdwUrlLength += lstrlenW(scheme);
}
(*lpdwUrlLength)++; /* ':' */
if (!scheme_is_opaque(nScheme) || lpUrlComponents->lpszHostName)
*lpdwUrlLength += strlen("//");
if (lpUrlComponents->lpszUserName)
{
*lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, UserName);
*lpdwUrlLength += strlen("@");
}
else
{
if (lpUrlComponents->lpszPassword)
{
INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
if (lpUrlComponents->lpszPassword)
{
*lpdwUrlLength += strlen(":");
*lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, Password);
}
if (lpUrlComponents->lpszHostName)
{
*lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, HostName);
if (!url_uses_default_port(nScheme, lpUrlComponents->nPort))
{
WCHAR port[MAX_WORD_DIGITS + 1];
_ltow(lpUrlComponents->nPort, port, 10);
*lpdwUrlLength += lstrlenW(port);
*lpdwUrlLength += strlen(":");
}
if (lpUrlComponents->lpszUrlPath && *lpUrlComponents->lpszUrlPath != '/')
(*lpdwUrlLength)++; /* '/' */
}
if (lpUrlComponents->lpszUrlPath)
*lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, UrlPath);
if (lpUrlComponents->lpszExtraInfo)
*lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, ExtraInfo);
return TRUE;
}
static void convert_urlcomp_atow(LPURL_COMPONENTSA lpUrlComponents, LPURL_COMPONENTSW urlCompW)
{
INT len;
ZeroMemory(urlCompW, sizeof(URL_COMPONENTSW));
urlCompW->dwStructSize = sizeof(URL_COMPONENTSW);
urlCompW->dwSchemeLength = lpUrlComponents->dwSchemeLength;
urlCompW->nScheme = lpUrlComponents->nScheme;
urlCompW->dwHostNameLength = lpUrlComponents->dwHostNameLength;
urlCompW->nPort = lpUrlComponents->nPort;
urlCompW->dwUserNameLength = lpUrlComponents->dwUserNameLength;
urlCompW->dwPasswordLength = lpUrlComponents->dwPasswordLength;
urlCompW->dwUrlPathLength = lpUrlComponents->dwUrlPathLength;
urlCompW->dwExtraInfoLength = lpUrlComponents->dwExtraInfoLength;
if (lpUrlComponents->lpszScheme)
{
len = URL_GET_COMP_LENGTHA(lpUrlComponents, Scheme) + 1;
urlCompW->lpszScheme = malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszScheme,
-1, urlCompW->lpszScheme, len);
}
if (lpUrlComponents->lpszHostName)
{
len = URL_GET_COMP_LENGTHA(lpUrlComponents, HostName) + 1;
urlCompW->lpszHostName = malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszHostName,
-1, urlCompW->lpszHostName, len);
}
if (lpUrlComponents->lpszUserName)
{
len = URL_GET_COMP_LENGTHA(lpUrlComponents, UserName) + 1;
urlCompW->lpszUserName = malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUserName,
-1, urlCompW->lpszUserName, len);
}
if (lpUrlComponents->lpszPassword)
{
len = URL_GET_COMP_LENGTHA(lpUrlComponents, Password) + 1;
urlCompW->lpszPassword = malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszPassword,
-1, urlCompW->lpszPassword, len);
}
if (lpUrlComponents->lpszUrlPath)
{
len = URL_GET_COMP_LENGTHA(lpUrlComponents, UrlPath) + 1;
urlCompW->lpszUrlPath = malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUrlPath,
-1, urlCompW->lpszUrlPath, len);
}
if (lpUrlComponents->lpszExtraInfo)
{
len = URL_GET_COMP_LENGTHA(lpUrlComponents, ExtraInfo) + 1;
urlCompW->lpszExtraInfo = malloc(len * sizeof(WCHAR));
MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszExtraInfo,
-1, urlCompW->lpszExtraInfo, len);
}
}
/***********************************************************************
* InternetCreateUrlA (WININET.@)
*
* See InternetCreateUrlW.
*/
BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
LPSTR lpszUrl, LPDWORD lpdwUrlLength)
{
BOOL ret;
LPWSTR urlW = NULL;
URL_COMPONENTSW urlCompW;
TRACE("(%p,%ld,%p,%p)\n", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength);
if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
convert_urlcomp_atow(lpUrlComponents, &urlCompW);
if (lpszUrl)
urlW = malloc(*lpdwUrlLength * sizeof(WCHAR));
ret = InternetCreateUrlW(&urlCompW, dwFlags, urlW, lpdwUrlLength);
if (!ret && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
*lpdwUrlLength /= sizeof(WCHAR);
/* on success, lpdwUrlLength points to the size of urlW in WCHARS
* minus one, so add one to leave room for NULL terminator
*/
if (ret)
WideCharToMultiByte(CP_ACP, 0, urlW, -1, lpszUrl, *lpdwUrlLength + 1, NULL, NULL);
free(urlCompW.lpszScheme);
free(urlCompW.lpszHostName);
free(urlCompW.lpszUserName);
free(urlCompW.lpszPassword);
free(urlCompW.lpszUrlPath);
free(urlCompW.lpszExtraInfo);
free(urlW);
return ret;
}
/***********************************************************************
* InternetCreateUrlW (WININET.@)
*
* Creates a URL from its component parts.
*
* PARAMS
* lpUrlComponents [I] URL Components.
* dwFlags [I] Flags. See notes.
* lpszUrl [I] Buffer in which to store the created URL.
* lpdwUrlLength [I/O] On input, the length of the buffer pointed to by
* lpszUrl in characters. On output, the number of bytes
* required to store the URL including terminator.
*
* NOTES
*
* The dwFlags parameter can be zero or more of the following:
*|ICU_ESCAPE - Generates escape sequences for unsafe characters in the path and extra info of the URL.
*
* RETURNS
* TRUE on success
* FALSE on failure
*
*/
BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
{
DWORD dwLen;
INTERNET_SCHEME nScheme;
static const WCHAR slashSlashW[] = {'/','/'};
TRACE("(%p,%ld,%p,%p)\n", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength);
if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
{
INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!calc_url_length(lpUrlComponents, &dwLen))
return FALSE;
if (!lpszUrl || *lpdwUrlLength < dwLen)
{
*lpdwUrlLength = (dwLen + 1) * sizeof(WCHAR);
INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*lpdwUrlLength = dwLen;
lpszUrl[0] = 0x00;
dwLen = 0;
if (lpUrlComponents->lpszScheme)
{
dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Scheme);
memcpy(lpszUrl, lpUrlComponents->lpszScheme, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
nScheme = GetInternetSchemeW(lpUrlComponents->lpszScheme, dwLen);
}
else
{
LPCWSTR scheme;
nScheme = lpUrlComponents->nScheme;
if (nScheme == INTERNET_SCHEME_DEFAULT)
nScheme = INTERNET_SCHEME_HTTP;
scheme = INTERNET_GetSchemeString(nScheme);
dwLen = lstrlenW(scheme);
memcpy(lpszUrl, scheme, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
}
/* all schemes are followed by at least a colon */
*lpszUrl = ':';
lpszUrl++;
if (!scheme_is_opaque(nScheme) || lpUrlComponents->lpszHostName)
{
memcpy(lpszUrl, slashSlashW, sizeof(slashSlashW));
lpszUrl += ARRAY_SIZE(slashSlashW);
}
if (lpUrlComponents->lpszUserName)
{
dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, UserName);
memcpy(lpszUrl, lpUrlComponents->lpszUserName, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
if (lpUrlComponents->lpszPassword)
{
*lpszUrl = ':';
lpszUrl++;
dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Password);
memcpy(lpszUrl, lpUrlComponents->lpszPassword, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
}
*lpszUrl = '@';
lpszUrl++;
}
if (lpUrlComponents->lpszHostName)
{
dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, HostName);
memcpy(lpszUrl, lpUrlComponents->lpszHostName, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
if (!url_uses_default_port(nScheme, lpUrlComponents->nPort))
{
*lpszUrl++ = ':';
_ltow(lpUrlComponents->nPort, lpszUrl, 10);
lpszUrl += lstrlenW(lpszUrl);
}
/* add slash between hostname and path if necessary */
if (lpUrlComponents->lpszUrlPath && *lpUrlComponents->lpszUrlPath != '/')
{
*lpszUrl = '/';
lpszUrl++;
}
}
if (lpUrlComponents->lpszUrlPath)
{
dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, UrlPath);
memcpy(lpszUrl, lpUrlComponents->lpszUrlPath, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
}
if (lpUrlComponents->lpszExtraInfo)
{
dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, ExtraInfo);
memcpy(lpszUrl, lpUrlComponents->lpszExtraInfo, dwLen * sizeof(WCHAR));
lpszUrl += dwLen;
}
*lpszUrl = '\0';
return TRUE;
}
/***********************************************************************
* InternetConfirmZoneCrossingA (WININET.@)
*
*/
DWORD WINAPI InternetConfirmZoneCrossingA( HWND hWnd, LPSTR szUrlPrev, LPSTR szUrlNew, BOOL bPost )
{
FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_a(szUrlPrev), debugstr_a(szUrlNew), bPost);
return ERROR_SUCCESS;
}
/***********************************************************************
* InternetConfirmZoneCrossingW (WININET.@)
*
*/
DWORD WINAPI InternetConfirmZoneCrossingW( HWND hWnd, LPWSTR szUrlPrev, LPWSTR szUrlNew, BOOL bPost )
{
FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_w(szUrlPrev), debugstr_w(szUrlNew), bPost);
return ERROR_SUCCESS;
}
static DWORD zone_preference = 3;
/***********************************************************************
* PrivacySetZonePreferenceW (WININET.@)
*/
DWORD WINAPI PrivacySetZonePreferenceW( DWORD zone, DWORD type, DWORD template, LPCWSTR preference )
{
FIXME( "%lx %lx %lx %s: stub\n", zone, type, template, debugstr_w(preference) );
zone_preference = template;
return 0;
}
/***********************************************************************
* PrivacyGetZonePreferenceW (WININET.@)
*/
DWORD WINAPI PrivacyGetZonePreferenceW( DWORD zone, DWORD type, LPDWORD template,
LPWSTR preference, LPDWORD length )
{
FIXME( "%lx %lx %p %p %p: stub\n", zone, type, template, preference, length );
if (template) *template = zone_preference;
return 0;
}
/***********************************************************************
* InternetGetSecurityInfoByURLA (WININET.@)
*/
BOOL WINAPI InternetGetSecurityInfoByURLA(LPSTR lpszURL, PCCERT_CHAIN_CONTEXT *ppCertChain, DWORD *pdwSecureFlags)
{
WCHAR *url;
BOOL res;
TRACE("(%s %p %p)\n", debugstr_a(lpszURL), ppCertChain, pdwSecureFlags);
url = strdupAtoW(lpszURL);
if(!url)
return FALSE;
res = InternetGetSecurityInfoByURLW(url, ppCertChain, pdwSecureFlags);
free(url);
return res;
}
/***********************************************************************
* InternetGetSecurityInfoByURLW (WININET.@)
*/
BOOL WINAPI InternetGetSecurityInfoByURLW(LPCWSTR lpszURL, PCCERT_CHAIN_CONTEXT *ppCertChain, DWORD *pdwSecureFlags)
{
URL_COMPONENTSW url = {sizeof(url)};
server_t *server;
BOOL res;
TRACE("(%s %p %p)\n", debugstr_w(lpszURL), ppCertChain, pdwSecureFlags);
if (!ppCertChain && !pdwSecureFlags) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
url.dwHostNameLength = 1;
res = InternetCrackUrlW(lpszURL, 0, 0, &url);
if(!res || url.nScheme != INTERNET_SCHEME_HTTPS) {
SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
return FALSE;
}
server = get_server(substr(url.lpszHostName, url.dwHostNameLength), url.nPort, TRUE, FALSE);
if(!server) {
SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
return FALSE;
}
if(server->cert_chain) {
if(pdwSecureFlags)
*pdwSecureFlags = server->security_flags & _SECURITY_ERROR_FLAGS_MASK;
if(ppCertChain && !(*ppCertChain = CertDuplicateCertificateChain(server->cert_chain)))
res = FALSE;
}else {
SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
res = FALSE;
}
server_release(server);
return res;
}
DWORD WINAPI InternetDialA( HWND hwndParent, LPSTR lpszConnectoid, DWORD dwFlags,
DWORD_PTR* lpdwConnection, DWORD dwReserved )
{
FIXME("(%p, %p, 0x%08lx, %p, 0x%08lx) stub\n", hwndParent, lpszConnectoid, dwFlags,
lpdwConnection, dwReserved);
return ERROR_SUCCESS;
}
DWORD WINAPI InternetDialW( HWND hwndParent, LPWSTR lpszConnectoid, DWORD dwFlags,
DWORD_PTR* lpdwConnection, DWORD dwReserved )
{
FIXME("(%p, %p, 0x%08lx, %p, 0x%08lx) stub\n", hwndParent, lpszConnectoid, dwFlags,
lpdwConnection, dwReserved);
return ERROR_SUCCESS;
}
BOOL WINAPI InternetGoOnlineA( LPSTR lpszURL, HWND hwndParent, DWORD dwReserved )
{
FIXME("(%s, %p, 0x%08lx) stub\n", debugstr_a(lpszURL), hwndParent, dwReserved);
return TRUE;
}
BOOL WINAPI InternetGoOnlineW( LPWSTR lpszURL, HWND hwndParent, DWORD dwReserved )
{
FIXME("(%s, %p, 0x%08lx) stub\n", debugstr_w(lpszURL), hwndParent, dwReserved);
return TRUE;
}
DWORD WINAPI InternetHangUp( DWORD_PTR dwConnection, DWORD dwReserved )
{
FIXME("(0x%08Ix, 0x%08lx) stub\n", dwConnection, dwReserved);
return ERROR_SUCCESS;
}
BOOL WINAPI CreateMD5SSOHash( PWSTR pszChallengeInfo, PWSTR pwszRealm, PWSTR pwszTarget,
PBYTE pbHexHash )
{
FIXME("(%s, %s, %s, %p) stub\n", debugstr_w(pszChallengeInfo), debugstr_w(pwszRealm),
debugstr_w(pwszTarget), pbHexHash);
return FALSE;
}
BOOL WINAPI ResumeSuspendedDownload( HINTERNET hInternet, DWORD dwError )
{
FIXME("(%p, 0x%08lx) stub\n", hInternet, dwError);
return FALSE;
}
BOOL WINAPI InternetQueryFortezzaStatus(DWORD *a, DWORD_PTR b)
{
FIXME("(%p, %08Ix) stub\n", a, b);
return FALSE;
}
DWORD WINAPI ShowClientAuthCerts(HWND parent)
{
FIXME("%p: stub\n", parent);
return 0;
}